mirror of
https://gitee.com/winc-link/hummingbird.git
synced 2025-04-20 08:22:42 +00:00
commit
b382dfb8b7
3
.gitignore
vendored
3
.gitignore
vendored
@ -14,3 +14,6 @@ hummingbird
|
|||||||
## binary
|
## binary
|
||||||
cmd/hummingbird-core/hummingbird-core
|
cmd/hummingbird-core/hummingbird-core
|
||||||
cmd/mqtt-broker/mqtt-broker
|
cmd/mqtt-broker/mqtt-broker
|
||||||
|
|
||||||
|
kuiper
|
||||||
|
db-data/leveldb-core-data
|
@ -1,42 +0,0 @@
|
|||||||
# ----------------------------------------------------------------------------------
|
|
||||||
# Copyright 2018 Dell Technologies, Inc.
|
|
||||||
# Copyright 2018 Cavium
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# ----------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
ARG BUILDER_BASE=golang:latest
|
|
||||||
FROM ${BUILDER_BASE} AS builder
|
|
||||||
|
|
||||||
WORKDIR /edge
|
|
||||||
|
|
||||||
# gitlab
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build make cmd/mqtt-broker/mqtt-broker
|
|
||||||
|
|
||||||
#Next image - Copy built Go binary into new workspace
|
|
||||||
FROM alpine:3.16
|
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/var/cache/apk apk add --update --no-cache dumb-init
|
|
||||||
|
|
||||||
EXPOSE 58090
|
|
||||||
|
|
||||||
WORKDIR /
|
|
||||||
COPY --from=builder /edge/cmd/mqtt-broker/mqtt-broker /bin/
|
|
||||||
COPY --from=builder /edge/cmd/mqtt-broker/res/configuration.yml.dist /etc/emqtt-broker/res/configuration.yml
|
|
||||||
|
|
||||||
#RUN mkdir -p /logs/mqtt-broker
|
|
||||||
|
|
||||||
CMD ["/bin/sh", "-c", "/bin/mqtt-broker start -c=/etc/emqtt-broker/res/configuration.yml"]
|
|
@ -1,39 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright 2017 Dell Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
||||||
* in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
||||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
||||||
* or implied. See the License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
package initcmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/winc-link/hummingbird/cmd/mqtt-broker/mqttd"
|
|
||||||
"github.com/winc-link/hummingbird/cmd/mqtt-broker/mqttd/command"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
func must(err error) {
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprint(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Init(rootCmd *cobra.Command) {
|
|
||||||
configDir, err := mqttd.GetDefaultConfigDir()
|
|
||||||
must(err)
|
|
||||||
command.ConfigFile = path.Join(configDir, "configuration.yml")
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&command.ConfigFile, "config", "c", command.ConfigFile, "The configuration file path")
|
|
||||||
rootCmd.AddCommand(command.NewStartCmd())
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright 2017 Dell Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
||||||
* in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
||||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
||||||
* or implied. See the License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/winc-link/hummingbird/cmd/mqtt-broker/initcmd"
|
|
||||||
|
|
||||||
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence"
|
|
||||||
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/topicalias/fifo"
|
|
||||||
|
|
||||||
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/admin"
|
|
||||||
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/aplugin"
|
|
||||||
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/auth"
|
|
||||||
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/federation"
|
|
||||||
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
rootCmd = &cobra.Command{
|
|
||||||
Use: "mqttd",
|
|
||||||
Long: "This is a MQTT broker that fully implements MQTT V5.0 and V3.1.1 protocol",
|
|
||||||
Version: "",
|
|
||||||
}
|
|
||||||
enablePprof bool
|
|
||||||
pprofAddr = "127.0.0.1:60600"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if enablePprof {
|
|
||||||
go func() {
|
|
||||||
http.ListenAndServe(pprofAddr, nil)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
initcmd.Init(rootCmd)
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
|
||||||
fmt.Fprint(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
package command
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/pidfile"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ConfigFile string
|
|
||||||
logger *zap.Logger
|
|
||||||
)
|
|
||||||
|
|
||||||
func must(err error) {
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprint(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func installSignal(srv server.Server) {
|
|
||||||
// reload
|
|
||||||
reloadSignalCh := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(reloadSignalCh, syscall.SIGHUP)
|
|
||||||
|
|
||||||
// stop
|
|
||||||
stopSignalCh := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(stopSignalCh, os.Interrupt, syscall.SIGTERM)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-reloadSignalCh:
|
|
||||||
var c config.Config
|
|
||||||
var err error
|
|
||||||
c, err = config.ParseConfig(ConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("reload error", zap.Error(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
srv.ApplyConfig(c)
|
|
||||||
logger.Info("gmqtt reloaded")
|
|
||||||
case <-stopSignalCh:
|
|
||||||
err := srv.Stop(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err.Error())
|
|
||||||
//fmt.Fprint(os.Stderr, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetListeners(c config.Config) (tcpListeners []net.Listener, websockets []*server.WsServer, err error) {
|
|
||||||
for _, v := range c.Listeners {
|
|
||||||
var ln net.Listener
|
|
||||||
if v.Websocket != nil {
|
|
||||||
ws := &server.WsServer{
|
|
||||||
Server: &http.Server{Addr: v.Address},
|
|
||||||
Path: v.Websocket.Path,
|
|
||||||
}
|
|
||||||
if v.TLSOptions != nil {
|
|
||||||
ws.KeyFile = v.Key
|
|
||||||
ws.CertFile = v.Cert
|
|
||||||
}
|
|
||||||
websockets = append(websockets, ws)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if v.TLSOptions != nil {
|
|
||||||
var cert tls.Certificate
|
|
||||||
cert, err = tls.LoadX509KeyPair(v.Cert, v.Key)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ln, err = tls.Listen("tcp", v.Address, &tls.Config{
|
|
||||||
Certificates: []tls.Certificate{cert},
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
ln, err = net.Listen("tcp", v.Address)
|
|
||||||
}
|
|
||||||
tcpListeners = append(tcpListeners, ln)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStartCmd creates a *cobra.Command object for start command.
|
|
||||||
func NewStartCmd() *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "start",
|
|
||||||
Short: "Start gmqtt broker",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
var err error
|
|
||||||
must(err)
|
|
||||||
c, err := config.ParseConfig(ConfigFile)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
must(err)
|
|
||||||
} else {
|
|
||||||
must(err)
|
|
||||||
}
|
|
||||||
if c.PidFile != "" {
|
|
||||||
pid, err := pidfile.New(c.PidFile)
|
|
||||||
if err != nil {
|
|
||||||
must(fmt.Errorf("open pid file failed: %s", err))
|
|
||||||
}
|
|
||||||
defer pid.Remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
level, l, err := c.GetLogger(c.Log)
|
|
||||||
must(err)
|
|
||||||
logger = l
|
|
||||||
|
|
||||||
//db := mqttbroker.NewDatabase(c)
|
|
||||||
//err = db.InitDBClient(l)
|
|
||||||
//must(err)
|
|
||||||
|
|
||||||
tcpListeners, websockets, err := GetListeners(c)
|
|
||||||
must(err)
|
|
||||||
|
|
||||||
s := server.New(
|
|
||||||
server.WithConfig(c),
|
|
||||||
server.WithTCPListener(tcpListeners...),
|
|
||||||
server.WithWebsocketServer(websockets...),
|
|
||||||
server.WithLogger(&server.DefaultLogger{
|
|
||||||
Level: level,
|
|
||||||
Logger: l,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
err = s.Init()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go installSignal(s)
|
|
||||||
err = s.Run()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprint(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return cmd
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package mqttd
|
|
||||||
|
|
||||||
var (
|
|
||||||
DefaultConfigDir = "./res/"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetDefaultConfigDir() (string, error) {
|
|
||||||
return DefaultConfigDir, nil
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
// +build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"go/format"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tmpl = `//go:generate sh -c "cd ../../ && go run plugin_generate.go"
|
|
||||||
// generated by plugin_generate.go; DO NOT EDIT
|
|
||||||
|
|
||||||
package mqttd
|
|
||||||
|
|
||||||
import (
|
|
||||||
{{- range $index, $element := .}}
|
|
||||||
_ "{{$element}}"
|
|
||||||
{{- end}}
|
|
||||||
)
|
|
||||||
`
|
|
||||||
|
|
||||||
const (
|
|
||||||
pluginFile = "./mqttd/plugins.go"
|
|
||||||
pluginCfg = "plugin_imports.yml"
|
|
||||||
importPath = "gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/plugin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ymlCfg struct {
|
|
||||||
Packages []string `yaml:"packages"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
b, err := ioutil.ReadFile(pluginCfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("ReadFile error %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var cfg ymlCfg
|
|
||||||
err = yaml.Unmarshal(b, &cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unmarshal error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t, err := template.New("plugin_gen").Parse(tmpl)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Parse template error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range cfg.Packages {
|
|
||||||
if !strings.Contains(v, "/") {
|
|
||||||
cfg.Packages[k] = importPath + "/" + v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
log.Fatalf("read error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
err = t.Execute(buf, cfg.Packages)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("excute template error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rs, err := format.Source(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("format error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(pluginFile, rs, 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("writeFile error: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
packages:
|
|
||||||
- admin
|
|
||||||
# - federation
|
|
||||||
- aplugin
|
|
||||||
# for external plugin, use full import path
|
|
||||||
# - gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/plugin/prometheus
|
|
@ -1,22 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDszCCApugAwIBAgIJAJXKBu6eNV6YMA0GCSqGSIb3DQEBCwUAMHAxCzAJBgNV
|
|
||||||
BAYTAkNOMQswCQYDVQQIDAJaSjELMAkGA1UEBwwCSFoxCzAJBgNVBAoMAlRZMQsw
|
|
||||||
CQYDVQQLDAJUWTEOMAwGA1UEAwwFdGVkZ2UxHTAbBgkqhkiG9w0BCQEWDnRlZGdl
|
|
||||||
QHR1eWEuY29tMB4XDTIyMDEyNDA2NTQ0M1oXDTMyMDEyMjA2NTQ0M1owcDELMAkG
|
|
||||||
A1UEBhMCQ04xCzAJBgNVBAgMAlpKMQswCQYDVQQHDAJIWjELMAkGA1UECgwCVFkx
|
|
||||||
CzAJBgNVBAsMAlRZMQ4wDAYDVQQDDAV0ZWRnZTEdMBsGCSqGSIb3DQEJARYOdGVk
|
|
||||||
Z2VAdHV5YS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/IZfF
|
|
||||||
++ytZVIDbt5Ypz/55e0HTrq9jrpVOZAKSBbmSUryjpo8NfZoDp5QVZi4kSo1G0xV
|
|
||||||
Wf9C+5h13TFM2pDm9W9q4v8e3cB3Z+qK8nHn66xyQYnTihg8D9vyJHIQ2nirCVqW
|
|
||||||
HL2wYdakE0MojbVsQPWufYh84tWXyyUIo2W2ycoXmSfpWhb4LDEf4tcmDBNp2ydG
|
|
||||||
ef7MNbrS3t/h/iOzqjj7s+styiLyKjxE0oh1VfOOp8e9HPnh2EvaQwwTq91KRf+v
|
|
||||||
rl4DPZt93oMd9i28HuxBsWsE6eDRfYmF96ZoIXEh4ga9XWR8geuRCsTREQo7tqUX
|
|
||||||
gXFUXCe2Uo6R0uh9AgMBAAGjUDBOMB0GA1UdDgQWBBQayDeoKN44f/FV+Z6rv1vT
|
|
||||||
bsITvTAfBgNVHSMEGDAWgBQayDeoKN44f/FV+Z6rv1vTbsITvTAMBgNVHRMEBTAD
|
|
||||||
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB0G/AM7zU2USZj3C32zzzM6LQNx465x1i2
|
|
||||||
XgSw9ECM7M3ct6x859L6vKXWUm6+OQO7jm9xRRyDQIrCSQT66MCei5C+nqzyPIZA
|
|
||||||
zL5cV7bRXA39KBwThyZZqWl4bttp98UZnEbX6yICVEcjsnaIA2D0vh1zZar4Ilyq
|
|
||||||
mBl4NA13HTIcQ0s4Efuhdf5RdPw3ha2cjf74aNNj2WijAY4rKEVF1Buw/PrvJ7WR
|
|
||||||
hrQlNrv214e3hbjV99oiII8OLDT0oJApUbSr6ktjF26bAu929b3QDADEK9QpBYE1
|
|
||||||
brg3KD51xgD+HGKd3PVLqr60y7OQKKMHd8TrQK/ibQVgFdbE6/AG
|
|
||||||
-----END CERTIFICATE-----
|
|
@ -1,159 +0,0 @@
|
|||||||
# Path to pid file.
|
|
||||||
# If not set, there will be no pid file.
|
|
||||||
# pid_file: /var/run/mqttd.pid
|
|
||||||
|
|
||||||
listeners:
|
|
||||||
# bind address
|
|
||||||
- address: ":58090" # 58090
|
|
||||||
|
|
||||||
# - address: ":21883" # 21883
|
|
||||||
# tls:
|
|
||||||
# cacert: "/etc/mqtt-broker/ca.crt"
|
|
||||||
# cert: "/etc/mqtt-broker/server.pem"
|
|
||||||
# key: "/etc/mqtt-broker/server.key"
|
|
||||||
#
|
|
||||||
# cacert: "cmd/mqtt-broker/res/ca.crt"
|
|
||||||
# cert: "cmd/mqtt-broker/res/server.pem"
|
|
||||||
# key: "cmd/mqtt-broker/res/server.key"
|
|
||||||
#
|
|
||||||
# - address: ":28883" # 28883
|
|
||||||
# # websocket setting
|
|
||||||
# websocket:
|
|
||||||
# path: "/"
|
|
||||||
|
|
||||||
api:
|
|
||||||
grpc:
|
|
||||||
# The gRPC server listen address. Supports unix socket and tcp socket.
|
|
||||||
- address: "tcp://127.0.0.1:57090" # 57090
|
|
||||||
http:
|
|
||||||
# The HTTP server listen address. This is a reverse-proxy server in front of gRPC server.
|
|
||||||
- address: "tcp://127.0.0.1:57091" # 57091
|
|
||||||
map: "tcp://127.0.0.1:57090" # The backend gRPC server endpoint,
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
# The maximum session expiry interval in seconds.
|
|
||||||
session_expiry: 2h
|
|
||||||
# The interval time for session expiry checker to check whether there are expired sessions.
|
|
||||||
session_expiry_check_timer: 20s
|
|
||||||
# The maximum lifetime of the message in seconds.
|
|
||||||
# If a message in the queue is not sent in message_expiry time, it will be dropped, which means it will not be sent to the subscriber.
|
|
||||||
message_expiry: 2h
|
|
||||||
# The lifetime of the "inflight" message in seconds.
|
|
||||||
# If a "inflight" message is not acknowledged by a client in inflight_expiry time, it will be removed when the message queue is full.
|
|
||||||
inflight_expiry: 30s
|
|
||||||
# The maximum packet size that the server is willing to accept from the client.
|
|
||||||
max_packet_size: 268435456
|
|
||||||
# The maximum number of QoS 1 and QoS 2 publications that the server is willing to process concurrently for the client.
|
|
||||||
server_receive_maximum: 100
|
|
||||||
# The maximum keep alive time in seconds allows by the server.
|
|
||||||
# If the client requests a keepalive time bigger than MaxKeepalive,the server will use MaxKeepAlive as the keepalive time.
|
|
||||||
# In this case, if the client version is v5, the server will set MaxKeepalive into CONNACK to inform the client.
|
|
||||||
# But if the client version is 3.x, the server has no way to inform the client that the keepalive time has been changed.
|
|
||||||
max_keepalive: 300
|
|
||||||
# The highest value that the server will accept as a Topic Alias sent by the client.
|
|
||||||
# No-op if the client version is MQTTv3.x .
|
|
||||||
topic_alias_maximum: 10
|
|
||||||
# Whether the server supports Subscription Identifiers.
|
|
||||||
# No-op if the client version is MQTTv3.x .
|
|
||||||
subscription_identifier_available: true
|
|
||||||
# Whether the server supports Wildcard Subscriptions.
|
|
||||||
wildcard_subscription_available: true
|
|
||||||
# Whether the server supports Shared Subscriptions.
|
|
||||||
shared_subscription_available: true
|
|
||||||
# The highest QOS level permitted for a Publish.
|
|
||||||
maximum_qos: 2
|
|
||||||
# Whether the server supports retained messages.
|
|
||||||
retain_available: true
|
|
||||||
# The maximum queue length of the outgoing messages.
|
|
||||||
# If the queue is full, some message will be dropped.
|
|
||||||
# The message dropping strategy is described in the document of the persistence/queue.Store interface.
|
|
||||||
max_queued_messages: 1000
|
|
||||||
# The limits of inflight message length of the outgoing messages.
|
|
||||||
# Inflight message is also stored in the message queue, so it must be less than or equal to max_queued_messages.
|
|
||||||
# Inflight message is the QoS 1 or QoS 2 message that has been sent out to a client but not been acknowledged yet.
|
|
||||||
max_inflight: 100
|
|
||||||
# Whether to store QoS 0 message for a offline session.
|
|
||||||
queue_qos0_messages: true
|
|
||||||
# The delivery mode. The possible value can be "overlap" or "onlyonce".
|
|
||||||
# It is possible for a client’s subscriptions to overlap so that a published message might match multiple filters.
|
|
||||||
# When set to "overlap" , the server will deliver one message for each matching subscription and respecting the subscription’s QoS in each case.
|
|
||||||
# When set to "onlyonce", the server will deliver the message to the client respecting the maximum QoS of all the matching subscriptions.
|
|
||||||
delivery_mode: onlyonce
|
|
||||||
# Whether to allow a client to connect with empty client id.
|
|
||||||
allow_zero_length_clientid: true
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
type: memory # memory | redis
|
|
||||||
# The redis configuration only take effect when type == redis.
|
|
||||||
redis:
|
|
||||||
# redis server address
|
|
||||||
addr: "127.0.0.1:56379"
|
|
||||||
# the maximum number of idle connections in the redis connection pool.
|
|
||||||
max_idle: 1000
|
|
||||||
# the maximum number of connections allocated by the redis connection pool at a given time.
|
|
||||||
# If zero, there is no limit on the number of connections in the pool.
|
|
||||||
max_active: 0
|
|
||||||
# the connection idle timeout, connection will be closed after remaining idle for this duration. If the value is zero, then idle connections are not closed.
|
|
||||||
idle_timeout: 240s
|
|
||||||
password: "qqwihyzjb8l2sx0c"
|
|
||||||
# the number of the redis database.
|
|
||||||
database: 0
|
|
||||||
|
|
||||||
# The topic alias manager setting. The topic alias feature is introduced by MQTT V5.
|
|
||||||
# This setting is used to control how the broker manage topic alias.
|
|
||||||
topic_alias_manager:
|
|
||||||
# Currently, only FIFO strategy is supported.
|
|
||||||
type: fifo
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
aplugin:
|
|
||||||
# Password hash type. (plain | md5 | sha256 | bcrypt)
|
|
||||||
# Default to MD5.
|
|
||||||
hash: md5
|
|
||||||
# The file to store password. If it is a relative path, it locates in the same directory as the config file.
|
|
||||||
# (e.g: ./gmqtt_password => /etc/gmqtt/gmqtt_password.yml)
|
|
||||||
# Defaults to ./gmqtt_password.yml
|
|
||||||
# password_file:
|
|
||||||
federation:
|
|
||||||
# node_name is the unique identifier for the node in the federation. Defaults to hostname.
|
|
||||||
# node_name:
|
|
||||||
# fed_addr is the gRPC server listening address for the federation internal communication. Defaults to :8901
|
|
||||||
fed_addr: :8901
|
|
||||||
# advertise_fed_addr is used to change the federation gRPC server address that we advertise to other nodes in the cluster.
|
|
||||||
# Defaults to "fed_addr".However, in some cases, there may be a routable address that cannot be bound.
|
|
||||||
# If the port is missing, the default federation port (8901) will be used.
|
|
||||||
advertise_fed_addr: :8901
|
|
||||||
# gossip_addr is the address that the gossip will listen on, It is used for both UDP and TCP gossip. Defaults to :8902
|
|
||||||
gossip_addr: :8902
|
|
||||||
# advertise_gossip_addr is used to change the gossip server address that we advertise to other nodes in the cluster.
|
|
||||||
# Defaults to "GossipAddr" or the private IP address of the node if the IP in "GossipAddr" is 0.0.0.0.
|
|
||||||
# If the port is missing, the default gossip port (8902) will be used.
|
|
||||||
advertise_gossip_addr: :8902
|
|
||||||
|
|
||||||
# retry_join is the address of other nodes to join upon starting up.
|
|
||||||
# If port is missing, the default gossip port (8902) will be used.
|
|
||||||
#retry_join:
|
|
||||||
# - 127.0.0.1:8902
|
|
||||||
|
|
||||||
# rejoin_after_leave will be pass to "RejoinAfterLeave" in serf configuration.
|
|
||||||
# It controls our interaction with the snapshot file.
|
|
||||||
# When set to false (default), a leave causes a Serf to not rejoin the cluster until an explicit join is received.
|
|
||||||
# If this is set to true, we ignore the leave, and rejoin the cluster on start.
|
|
||||||
rejoin_after_leave: false
|
|
||||||
# snapshot_path will be pass to "SnapshotPath" in serf configuration.
|
|
||||||
# When Serf is started with a snapshot,it will attempt to join all the previously known nodes until one
|
|
||||||
# succeeds and will also avoid replaying old user events.
|
|
||||||
snapshot_path:
|
|
||||||
|
|
||||||
# plugin loading orders
|
|
||||||
plugin_order:
|
|
||||||
# Uncomment auth to enable authentication.
|
|
||||||
- aplugin
|
|
||||||
#- admin
|
|
||||||
#- federation
|
|
||||||
log:
|
|
||||||
level: debug # debug | info | warn | error
|
|
||||||
file_path: "./mqtt-broker/mqtt-broker.log"
|
|
||||||
# whether to dump MQTT packet in debug level
|
|
||||||
dump_packet: false
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
|||||||
# Path to pid file.
|
|
||||||
# If not set, there will be no pid file.
|
|
||||||
# pid_file: /var/run/mqttd.pid
|
|
||||||
|
|
||||||
listeners:
|
|
||||||
# bind address
|
|
||||||
- address: ":58090" # 58090
|
|
||||||
|
|
||||||
# - address: ":21883" # 21883
|
|
||||||
# tls:
|
|
||||||
# cacert: "/etc/mqtt-broker/ca.crt"
|
|
||||||
# cert: "/etc/mqtt-broker/server.pem"
|
|
||||||
# key: "/etc/mqtt-broker/server.key"
|
|
||||||
#
|
|
||||||
# cacert: "cmd/mqtt-broker/res/ca.crt"
|
|
||||||
# cert: "cmd/mqtt-broker/res/server.pem"
|
|
||||||
# key: "cmd/mqtt-broker/res/server.key"
|
|
||||||
#
|
|
||||||
# - address: ":28883" # 28883
|
|
||||||
# # websocket setting
|
|
||||||
# websocket:
|
|
||||||
# path: "/"
|
|
||||||
|
|
||||||
api:
|
|
||||||
grpc:
|
|
||||||
# The gRPC server listen address. Supports unix socket and tcp socket.
|
|
||||||
- address: "tcp://127.0.0.1:57090" # 57090
|
|
||||||
http:
|
|
||||||
# The HTTP server listen address. This is a reverse-proxy server in front of gRPC server.
|
|
||||||
- address: "tcp://127.0.0.1:57091" # 57091
|
|
||||||
map: "tcp://127.0.0.1:57090" # The backend gRPC server endpoint,
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
# The maximum session expiry interval in seconds.
|
|
||||||
session_expiry: 2h
|
|
||||||
# The interval time for session expiry checker to check whether there are expired sessions.
|
|
||||||
session_expiry_check_timer: 20s
|
|
||||||
# The maximum lifetime of the message in seconds.
|
|
||||||
# If a message in the queue is not sent in message_expiry time, it will be dropped, which means it will not be sent to the subscriber.
|
|
||||||
message_expiry: 2h
|
|
||||||
# The lifetime of the "inflight" message in seconds.
|
|
||||||
# If a "inflight" message is not acknowledged by a client in inflight_expiry time, it will be removed when the message queue is full.
|
|
||||||
inflight_expiry: 30s
|
|
||||||
# The maximum packet size that the server is willing to accept from the client.
|
|
||||||
max_packet_size: 268435456
|
|
||||||
# The maximum number of QoS 1 and QoS 2 publications that the server is willing to process concurrently for the client.
|
|
||||||
server_receive_maximum: 100
|
|
||||||
# The maximum keep alive time in seconds allows by the server.
|
|
||||||
# If the client requests a keepalive time bigger than MaxKeepalive,the server will use MaxKeepAlive as the keepalive time.
|
|
||||||
# In this case, if the client version is v5, the server will set MaxKeepalive into CONNACK to inform the client.
|
|
||||||
# But if the client version is 3.x, the server has no way to inform the client that the keepalive time has been changed.
|
|
||||||
max_keepalive: 300
|
|
||||||
# The highest value that the server will accept as a Topic Alias sent by the client.
|
|
||||||
# No-op if the client version is MQTTv3.x .
|
|
||||||
topic_alias_maximum: 10
|
|
||||||
# Whether the server supports Subscription Identifiers.
|
|
||||||
# No-op if the client version is MQTTv3.x .
|
|
||||||
subscription_identifier_available: true
|
|
||||||
# Whether the server supports Wildcard Subscriptions.
|
|
||||||
wildcard_subscription_available: true
|
|
||||||
# Whether the server supports Shared Subscriptions.
|
|
||||||
shared_subscription_available: true
|
|
||||||
# The highest QOS level permitted for a Publish.
|
|
||||||
maximum_qos: 2
|
|
||||||
# Whether the server supports retained messages.
|
|
||||||
retain_available: true
|
|
||||||
# The maximum queue length of the outgoing messages.
|
|
||||||
# If the queue is full, some message will be dropped.
|
|
||||||
# The message dropping strategy is described in the document of the persistence/queue.Store interface.
|
|
||||||
max_queued_messages: 1000
|
|
||||||
# The limits of inflight message length of the outgoing messages.
|
|
||||||
# Inflight message is also stored in the message queue, so it must be less than or equal to max_queued_messages.
|
|
||||||
# Inflight message is the QoS 1 or QoS 2 message that has been sent out to a client but not been acknowledged yet.
|
|
||||||
max_inflight: 100
|
|
||||||
# Whether to store QoS 0 message for a offline session.
|
|
||||||
queue_qos0_messages: true
|
|
||||||
# The delivery mode. The possible value can be "overlap" or "onlyonce".
|
|
||||||
# It is possible for a client’s subscriptions to overlap so that a published message might match multiple filters.
|
|
||||||
# When set to "overlap" , the server will deliver one message for each matching subscription and respecting the subscription’s QoS in each case.
|
|
||||||
# When set to "onlyonce", the server will deliver the message to the client respecting the maximum QoS of all the matching subscriptions.
|
|
||||||
delivery_mode: onlyonce
|
|
||||||
# Whether to allow a client to connect with empty client id.
|
|
||||||
allow_zero_length_clientid: true
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
type: memory # memory | redis
|
|
||||||
# The redis configuration only take effect when type == redis.
|
|
||||||
redis:
|
|
||||||
# redis server address
|
|
||||||
addr: "127.0.0.1:56379"
|
|
||||||
# the maximum number of idle connections in the redis connection pool.
|
|
||||||
max_idle: 1000
|
|
||||||
# the maximum number of connections allocated by the redis connection pool at a given time.
|
|
||||||
# If zero, there is no limit on the number of connections in the pool.
|
|
||||||
max_active: 0
|
|
||||||
# the connection idle timeout, connection will be closed after remaining idle for this duration. If the value is zero, then idle connections are not closed.
|
|
||||||
idle_timeout: 240s
|
|
||||||
password: "qqwihyzjb8l2sx0c"
|
|
||||||
# the number of the redis database.
|
|
||||||
database: 0
|
|
||||||
|
|
||||||
# The topic alias manager setting. The topic alias feature is introduced by MQTT V5.
|
|
||||||
# This setting is used to control how the broker manage topic alias.
|
|
||||||
topic_alias_manager:
|
|
||||||
# Currently, only FIFO strategy is supported.
|
|
||||||
type: fifo
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
aplugin:
|
|
||||||
# Password hash type. (plain | md5 | sha256 | bcrypt)
|
|
||||||
# Default to MD5.
|
|
||||||
hash: md5
|
|
||||||
# The file to store password. If it is a relative path, it locates in the same directory as the config file.
|
|
||||||
# (e.g: ./gmqtt_password => /etc/gmqtt/gmqtt_password.yml)
|
|
||||||
# Defaults to ./gmqtt_password.yml
|
|
||||||
# password_file:
|
|
||||||
federation:
|
|
||||||
# node_name is the unique identifier for the node in the federation. Defaults to hostname.
|
|
||||||
# node_name:
|
|
||||||
# fed_addr is the gRPC server listening address for the federation internal communication. Defaults to :8901
|
|
||||||
fed_addr: :8901
|
|
||||||
# advertise_fed_addr is used to change the federation gRPC server address that we advertise to other nodes in the cluster.
|
|
||||||
# Defaults to "fed_addr".However, in some cases, there may be a routable address that cannot be bound.
|
|
||||||
# If the port is missing, the default federation port (8901) will be used.
|
|
||||||
advertise_fed_addr: :8901
|
|
||||||
# gossip_addr is the address that the gossip will listen on, It is used for both UDP and TCP gossip. Defaults to :8902
|
|
||||||
gossip_addr: :8902
|
|
||||||
# advertise_gossip_addr is used to change the gossip server address that we advertise to other nodes in the cluster.
|
|
||||||
# Defaults to "GossipAddr" or the private IP address of the node if the IP in "GossipAddr" is 0.0.0.0.
|
|
||||||
# If the port is missing, the default gossip port (8902) will be used.
|
|
||||||
advertise_gossip_addr: :8902
|
|
||||||
|
|
||||||
# retry_join is the address of other nodes to join upon starting up.
|
|
||||||
# If port is missing, the default gossip port (8902) will be used.
|
|
||||||
#retry_join:
|
|
||||||
# - 127.0.0.1:8902
|
|
||||||
|
|
||||||
# rejoin_after_leave will be pass to "RejoinAfterLeave" in serf configuration.
|
|
||||||
# It controls our interaction with the snapshot file.
|
|
||||||
# When set to false (default), a leave causes a Serf to not rejoin the cluster until an explicit join is received.
|
|
||||||
# If this is set to true, we ignore the leave, and rejoin the cluster on start.
|
|
||||||
rejoin_after_leave: false
|
|
||||||
# snapshot_path will be pass to "SnapshotPath" in serf configuration.
|
|
||||||
# When Serf is started with a snapshot,it will attempt to join all the previously known nodes until one
|
|
||||||
# succeeds and will also avoid replaying old user events.
|
|
||||||
snapshot_path:
|
|
||||||
|
|
||||||
# plugin loading orders
|
|
||||||
plugin_order:
|
|
||||||
# Uncomment auth to enable authentication.
|
|
||||||
- aplugin
|
|
||||||
#- admin
|
|
||||||
#- federation
|
|
||||||
log:
|
|
||||||
level: debug # debug | info | warn | error
|
|
||||||
file_path: "/logs/mqtt-broker/mqtt-broker.log"
|
|
||||||
# whether to dump MQTT packet in debug level
|
|
||||||
dump_packet: false
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
# This is a sample plain password file for the auth plugin.
|
|
||||||
- username: root
|
|
||||||
password: root
|
|
@ -1,14 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE REQUEST-----
|
|
||||||
MIICFTCCAX4CAQAwcDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAlpKMQswCQYDVQQH
|
|
||||||
DAJIWjELMAkGA1UECgwCVFkxCzAJBgNVBAsMAlRZMQ4wDAYDVQQDDAV0ZWRnZTEd
|
|
||||||
MBsGCSqGSIb3DQEJARYOdGVkZ2VAdHV5YS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
|
|
||||||
gY0AMIGJAoGBAMe1bzSZLfvqrBeBOgxAdaDqh8fWudqeb0wqC+ZSZg4uH+WEG4Hu
|
|
||||||
rMbt4B+b1U98ctpA/aOEgnZiV1z79w8Rm9ENvfCUOKJ8uJyVf2usAdR/HkudDOhU
|
|
||||||
KlnvXaCd5t99gi8pyBmYkaXf82ya7CN97f/Y35zNIcWTJhJmYmd3N6FRAgMBAAGg
|
|
||||||
ZTARBgkqhkiG9w0BCQIxBAwCdHkwFQYJKoZIhvcNAQkHMQgMBmJleW9uZDA5Bgkq
|
|
||||||
hkiG9w0BCQ4xLDAqMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMBAGA1UdEQQJMAeC
|
|
||||||
BXRlZGdlMA0GCSqGSIb3DQEBCwUAA4GBADSeemIkKCvrfOz+m0AxQN/9L8SEqdHB
|
|
||||||
l8YBaHSHgdxq6666ENPz5o2uPNnu6qYaBZUMOZ5223Sx2MJPNDAxemFnOw7YbnCV
|
|
||||||
jIPwI3O9KIFDZ+tmhEIVHSlqRFphYNIWAVVFBsdNkse1gLTLLBLKfbsCZeoD4Dz2
|
|
||||||
mc3JPZjeo4Mq
|
|
||||||
-----END CERTIFICATE REQUEST-----
|
|
@ -1,16 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMe1bzSZLfvqrBeB
|
|
||||||
OgxAdaDqh8fWudqeb0wqC+ZSZg4uH+WEG4HurMbt4B+b1U98ctpA/aOEgnZiV1z7
|
|
||||||
9w8Rm9ENvfCUOKJ8uJyVf2usAdR/HkudDOhUKlnvXaCd5t99gi8pyBmYkaXf82ya
|
|
||||||
7CN97f/Y35zNIcWTJhJmYmd3N6FRAgMBAAECgYBVCLMGIWcMCdsm0vZlexja4KHZ
|
|
||||||
/Fr8dFONiaWxd0pPJWKddofD5l2ZAnZY3yCPjLzWo6+b7XMjdzIdvIdw2h2OwGpE
|
|
||||||
kPQST1lkN5VlPIG67jwmIyJVw1LBAqknmqRFLjJ8NcJRttNjYjEkpetMOq1rM3Di
|
|
||||||
90mY3lBLT2g5lZa0pQJBAOr0lEPC3WJMq1N04wzqM6h6y8FUKhGZDawl1HGne9bs
|
|
||||||
4IDzVEhiCT9VvN3eoX+bk6av1/uZOxHY78j81q8KzY8CQQDZmKpPOZFeBK8dIOt7
|
|
||||||
L4XB1NMVAkOy4UFZ1I9lpn9OVSQEPrnV0oyMnKIIMCzGy4nnFmrY/u4LKpuYSoVO
|
|
||||||
lvMfAkAHwhOzORf+SvHNS6rDnmgeRA++Tn0lH5yn9ofRSOp56lBvcZly2mnbwYT+
|
|
||||||
/n7uq8BwXJYRJLoimLsyM8cS+JRZAkAp40Glzqc1OiGbseKi7BsLnTSlLrJplQNH
|
|
||||||
j6urHcoUAj/UsV6E0utLhjuK5/s2qaf6XE5lR237qFAbmPzgjB5xAkAP7H0cfdzs
|
|
||||||
X9gCe4RqQgIuJzK6Y59GkVeWVT8lScL9FyWm2JmeGh907HgnTXt6t8Hhk23JxD3x
|
|
||||||
KNcIk5xzRaOO
|
|
||||||
-----END PRIVATE KEY-----
|
|
@ -1,19 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDCzCCAfOgAwIBAgIJAMMZRyEj7GMLMA0GCSqGSIb3DQEBCwUAMHAxCzAJBgNV
|
|
||||||
BAYTAkNOMQswCQYDVQQIDAJaSjELMAkGA1UEBwwCSFoxCzAJBgNVBAoMAlRZMQsw
|
|
||||||
CQYDVQQLDAJUWTEOMAwGA1UEAwwFdGVkZ2UxHTAbBgkqhkiG9w0BCQEWDnRlZGdl
|
|
||||||
QHR1eWEuY29tMB4XDTIyMDEyNDA3MDAxNVoXDTMyMDEyMjA3MDAxNVowcDELMAkG
|
|
||||||
A1UEBhMCQ04xCzAJBgNVBAgMAlpKMQswCQYDVQQHDAJIWjELMAkGA1UECgwCVFkx
|
|
||||||
CzAJBgNVBAsMAlRZMQ4wDAYDVQQDDAV0ZWRnZTEdMBsGCSqGSIb3DQEJARYOdGVk
|
|
||||||
Z2VAdHV5YS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMe1bzSZLfvq
|
|
||||||
rBeBOgxAdaDqh8fWudqeb0wqC+ZSZg4uH+WEG4HurMbt4B+b1U98ctpA/aOEgnZi
|
|
||||||
V1z79w8Rm9ENvfCUOKJ8uJyVf2usAdR/HkudDOhUKlnvXaCd5t99gi8pyBmYkaXf
|
|
||||||
82ya7CN97f/Y35zNIcWTJhJmYmd3N6FRAgMBAAGjLDAqMAkGA1UdEwQCMAAwCwYD
|
|
||||||
VR0PBAQDAgXgMBAGA1UdEQQJMAeCBXRlZGdlMA0GCSqGSIb3DQEBCwUAA4IBAQA5
|
|
||||||
htWsbfo7XedP2DBbVRXWFhEw7RPFfmyFMgzQq3aifnNB93xpDRwauXH5k6TEsiIO
|
|
||||||
OKjQit9aiSA28sTad6k6S09SwJokeQ9l14T3vVVMdDVJCw1Hq/mEhgoGgpYM+om0
|
|
||||||
t/gl7e4FHL0AH6vcAyO70Q4uVRGpnm6Ehp8MxW0f/uip6TLxSj3lTitkCytMSGMK
|
|
||||||
WhvTLy8gsD9sSkiZUL/jknVkSp5An3roayWZLZucPV0E2rINchRcMcrrY1UkeYu1
|
|
||||||
HB94dGg2U7R7Qj0eJBdxJN0uCY5n02pBXabJXRtwvOReHsW6Qoo50MhWEd2sazCA
|
|
||||||
HwMRWr6g8aAun7QJfySG
|
|
||||||
-----END CERTIFICATE-----
|
|
32
go.mod
32
go.mod
@ -15,17 +15,9 @@ require (
|
|||||||
github.com/go-gormigrate/gormigrate/v2 v2.0.0
|
github.com/go-gormigrate/gormigrate/v2 v2.0.0
|
||||||
github.com/gogf/gf/v2 v2.5.2
|
github.com/gogf/gf/v2 v2.5.2
|
||||||
github.com/golang/mock v1.5.0
|
github.com/golang/mock v1.5.0
|
||||||
github.com/golang/protobuf v1.5.2
|
|
||||||
github.com/gomodule/redigo v1.8.9
|
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/schema v1.2.0
|
github.com/gorilla/schema v1.2.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0
|
|
||||||
github.com/hashicorp/logutils v1.0.0
|
|
||||||
github.com/hashicorp/serf v0.8.2
|
|
||||||
github.com/hpcloud/tail v1.0.0
|
github.com/hpcloud/tail v1.0.0
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
|
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
|
||||||
github.com/jinzhu/gorm v1.9.16
|
github.com/jinzhu/gorm v1.9.16
|
||||||
@ -38,7 +30,6 @@ require (
|
|||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v3 v3.22.2
|
github.com/shirou/gopsutil/v3 v3.22.2
|
||||||
github.com/spf13/cobra v1.5.0
|
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.2
|
||||||
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
|
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
|
||||||
@ -57,11 +48,9 @@ require (
|
|||||||
golang.org/x/sys v0.10.0
|
golang.org/x/sys v0.10.0
|
||||||
golang.org/x/text v0.11.0
|
golang.org/x/text v0.11.0
|
||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
|
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
|
||||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c
|
|
||||||
google.golang.org/grpc v1.49.0
|
google.golang.org/grpc v1.49.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gorm.io/driver/mysql v1.0.1
|
gorm.io/driver/mysql v1.0.1
|
||||||
gorm.io/driver/sqlite v1.3.6
|
gorm.io/driver/sqlite v1.3.6
|
||||||
@ -71,9 +60,6 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
|
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
|
||||||
github.com/clbanning/mxj v1.8.4 // indirect
|
github.com/clbanning/mxj v1.8.4 // indirect
|
||||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
@ -94,17 +80,10 @@ require (
|
|||||||
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
||||||
github.com/goccy/go-json v0.9.11 // indirect
|
github.com/goccy/go-json v0.9.11 // 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/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||||
github.com/google/btree v1.0.0 // indirect
|
|
||||||
github.com/google/go-querystring v1.0.0 // indirect
|
github.com/google/go-querystring v1.0.0 // indirect
|
||||||
github.com/grokify/html-strip-tags-go v0.0.1 // indirect
|
github.com/grokify/html-strip-tags-go v0.0.1 // indirect
|
||||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.3 // indirect
|
|
||||||
github.com/hashicorp/go-multierror v1.0.0 // indirect
|
|
||||||
github.com/hashicorp/golang-lru v0.5.1 // indirect
|
|
||||||
github.com/hashicorp/memberlist v0.1.3 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
@ -117,8 +96,6 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.12 // indirect
|
github.com/mattn/go-sqlite3 v1.14.12 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
|
||||||
github.com/miekg/dns v1.0.14 // indirect
|
|
||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // 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
|
||||||
@ -130,14 +107,9 @@ require (
|
|||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
|
||||||
github.com/richardlehane/mscfb v1.0.3 // indirect
|
github.com/richardlehane/mscfb v1.0.3 // indirect
|
||||||
github.com/richardlehane/msoleps v1.0.1 // indirect
|
github.com/richardlehane/msoleps v1.0.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
github.com/rivo/uniseg v0.4.4 // indirect
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
||||||
github.com/tklauser/numcpus v0.3.0 // indirect
|
github.com/tklauser/numcpus v0.3.0 // indirect
|
||||||
@ -151,7 +123,9 @@ require (
|
|||||||
golang.org/x/net v0.12.0 // indirect
|
golang.org/x/net v0.12.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gotest.tools/v3 v3.3.0 // indirect
|
gotest.tools/v3 v3.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2018 DrmagicE
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,77 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// API is the configuration for API server.
|
|
||||||
// The API server use gRPC-gateway to provide both gRPC and HTTP endpoints.
|
|
||||||
type API struct {
|
|
||||||
// GRPC is the gRPC endpoint configuration.
|
|
||||||
GRPC []*Endpoint `yaml:"grpc"`
|
|
||||||
// HTTP is the HTTP endpoint configuration.
|
|
||||||
HTTP []*Endpoint `yaml:"http"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Endpoint represents a gRPC or HTTP server endpoint.
|
|
||||||
type Endpoint struct {
|
|
||||||
// Address is the bind address of the endpoint.
|
|
||||||
// Format: [tcp|unix://][<host>]:<port>
|
|
||||||
// e.g :
|
|
||||||
// * unix:///var/run/mqttd.sock
|
|
||||||
// * tcp://127.0.0.1:8080
|
|
||||||
// * :8081 (equal to tcp://:8081)
|
|
||||||
Address string `yaml:"address"`
|
|
||||||
// Map maps the HTTP endpoint to gRPC endpoint.
|
|
||||||
// Must be set if the endpoint is representing a HTTP endpoint.
|
|
||||||
Map string `yaml:"map"`
|
|
||||||
// TLS is the tls configuration.
|
|
||||||
TLS *TLSOptions `yaml:"tls"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var DefaultAPI API
|
|
||||||
|
|
||||||
func (a API) validateAddress(address string, fieldName string) error {
|
|
||||||
if address == "" {
|
|
||||||
return fmt.Errorf("%s cannot be empty", fieldName)
|
|
||||||
}
|
|
||||||
epParts := strings.SplitN(address, "://", 2)
|
|
||||||
if len(epParts) == 1 && epParts[0] != "" {
|
|
||||||
epParts = []string{"tcp", epParts[0]}
|
|
||||||
}
|
|
||||||
if len(epParts) != 0 {
|
|
||||||
switch epParts[0] {
|
|
||||||
case "tcp":
|
|
||||||
_, _, err := net.SplitHostPort(epParts[1])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid %s: %s", fieldName, err.Error())
|
|
||||||
}
|
|
||||||
case "unix":
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("invalid %s schema: %s", fieldName, epParts[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a API) Validate() error {
|
|
||||||
for _, v := range a.GRPC {
|
|
||||||
err := a.validateAddress(v.Address, "endpoint")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, v := range a.HTTP {
|
|
||||||
err := a.validateAddress(v.Address, "endpoint")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = a.validateAddress(v.Map, "map")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAPI_Validate(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
|
|
||||||
tt := []struct {
|
|
||||||
cfg API
|
|
||||||
valid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
cfg: API{
|
|
||||||
GRPC: []*Endpoint{
|
|
||||||
{
|
|
||||||
Address: "udp://127.0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HTTP: []*Endpoint{
|
|
||||||
{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
valid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cfg: API{
|
|
||||||
GRPC: []*Endpoint{
|
|
||||||
{
|
|
||||||
Address: "tcp://127.0.0.1:1234",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HTTP: []*Endpoint{
|
|
||||||
{
|
|
||||||
Address: "udp://127.0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
valid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cfg: API{
|
|
||||||
GRPC: []*Endpoint{
|
|
||||||
{
|
|
||||||
Address: "tcp://127.0.0.1:1234",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cfg: API{
|
|
||||||
GRPC: []*Endpoint{
|
|
||||||
{
|
|
||||||
Address: "tcp://127.0.0.1:1234",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HTTP: []*Endpoint{
|
|
||||||
{
|
|
||||||
Address: "tcp://127.0.0.1:1235",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
valid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cfg: API{
|
|
||||||
GRPC: []*Endpoint{
|
|
||||||
{
|
|
||||||
Address: "unix:///var/run/mqttd.sock",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HTTP: []*Endpoint{
|
|
||||||
{
|
|
||||||
Address: "tcp://127.0.0.1:1235",
|
|
||||||
Map: "unix:///var/run/mqttd.sock",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
err := v.cfg.Validate()
|
|
||||||
if v.valid {
|
|
||||||
a.NoError(err)
|
|
||||||
} else {
|
|
||||||
a.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,323 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"go.uber.org/zap/zapcore"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
pkgconfig "github.com/winc-link/hummingbird/internal/pkg/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
const EdgeMqttBroker = "mqtt-broker"
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultPluginConfig = make(map[string]Configuration)
|
|
||||||
configFileFullPath string
|
|
||||||
config Config
|
|
||||||
)
|
|
||||||
|
|
||||||
// Configuration is the interface that enable the implementation to parse config from the global config file.
|
|
||||||
// Plugin admin and prometheus are two examples.
|
|
||||||
type Configuration interface {
|
|
||||||
// Validate validates the configuration.
|
|
||||||
// If returns error, the broker will not start.
|
|
||||||
Validate() error
|
|
||||||
// Unmarshaler defined how to unmarshal YAML into the config structure.
|
|
||||||
yaml.Unmarshaler
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterDefaultPluginConfig registers the default configuration for the given plugin.
|
|
||||||
func RegisterDefaultPluginConfig(name string, config Configuration) {
|
|
||||||
if _, ok := defaultPluginConfig[name]; ok {
|
|
||||||
panic(fmt.Sprintf("duplicated default config for %s plugin", name))
|
|
||||||
}
|
|
||||||
defaultPluginConfig[name] = config
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultConfig return the default configuration.
|
|
||||||
// If config file is not provided, mqttd will start with DefaultConfig.
|
|
||||||
func DefaultConfig() Config {
|
|
||||||
c := Config{
|
|
||||||
Listeners: DefaultListeners,
|
|
||||||
MQTT: DefaultMQTTConfig,
|
|
||||||
API: DefaultAPI,
|
|
||||||
Log: LogConfig{
|
|
||||||
Level: "info",
|
|
||||||
FilePath: "/var/tedge/logs/mqtt-broker.log",
|
|
||||||
},
|
|
||||||
Plugins: make(pluginConfig),
|
|
||||||
PluginOrder: []string{"aplugin"},
|
|
||||||
Persistence: DefaultPersistenceConfig,
|
|
||||||
TopicAliasManager: DefaultTopicAliasManager,
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, v := range defaultPluginConfig {
|
|
||||||
c.Plugins[name] = v
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
var DefaultListeners = []*ListenerConfig{
|
|
||||||
{
|
|
||||||
Address: "0.0.0.0:58090",
|
|
||||||
TLSOptions: nil,
|
|
||||||
Websocket: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: "0.0.0.0:58091",
|
|
||||||
Websocket: &WebsocketOptions{
|
|
||||||
Path: "/",
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Address: "0.0.0.0:21883",
|
|
||||||
TLSOptions: &TLSOptions{
|
|
||||||
CACert: "/etc/tedge-mqtt-broker/ca.crt",
|
|
||||||
Cert: "/etc/tedge-mqtt-broker/server.pem",
|
|
||||||
Key: "/etc/tedge-mqtt-broker/server.key",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogConfig is use to configure the log behaviors.
|
|
||||||
type LogConfig struct {
|
|
||||||
// Level is the log level. Possible values: debug, info, warn, error
|
|
||||||
Level string `yaml:"level"`
|
|
||||||
FilePath string `yaml:"file_path"`
|
|
||||||
// DumpPacket indicates whether to dump MQTT packet in debug level.
|
|
||||||
DumpPacket bool `yaml:"dump_packet"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l LogConfig) Validate() error {
|
|
||||||
level := strings.ToLower(l.Level)
|
|
||||||
if level != "debug" && level != "info" && level != "warn" && level != "error" {
|
|
||||||
return fmt.Errorf("invalid log level: %s", l.Level)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// pluginConfig stores the plugin default configuration, key by the plugin name.
|
|
||||||
// If the plugin has default configuration, it should call RegisterDefaultPluginConfig in it's init function to register.
|
|
||||||
type pluginConfig map[string]Configuration
|
|
||||||
|
|
||||||
func (p pluginConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
for _, v := range p {
|
|
||||||
err := unmarshal(v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config is the configration for mqttd.
|
|
||||||
type Config struct {
|
|
||||||
Listeners []*ListenerConfig `yaml:"listeners"`
|
|
||||||
API API `yaml:"api"`
|
|
||||||
MQTT MQTT `yaml:"mqttclient,omitempty"`
|
|
||||||
GRPC GRPC `yaml:"gRPC"`
|
|
||||||
Log LogConfig `yaml:"log"`
|
|
||||||
PidFile string `yaml:"pid_file"`
|
|
||||||
ConfigDir string `yaml:"config_dir"`
|
|
||||||
Plugins pluginConfig `yaml:"plugins"`
|
|
||||||
// PluginOrder is a slice that contains the name of the plugin which will be loaded.
|
|
||||||
// Giving a correct order to the slice is significant,
|
|
||||||
// because it represents the loading order which affect the behavior of the broker.
|
|
||||||
PluginOrder []string `yaml:"plugin_order"`
|
|
||||||
Persistence Persistence `yaml:"persistence"`
|
|
||||||
TopicAliasManager TopicAliasManager `yaml:"topic_alias_manager"`
|
|
||||||
Database pkgconfig.Database `yaml:"data_base"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GRPC struct {
|
|
||||||
Endpoint string `yaml:"endpoint"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TLSOptions struct {
|
|
||||||
// CACert is the trust CA certificate file.
|
|
||||||
CACert string `yaml:"cacert"`
|
|
||||||
// Cert is the path to certificate file.
|
|
||||||
Cert string `yaml:"cert"`
|
|
||||||
// Key is the path to key file.
|
|
||||||
Key string `yaml:"key"`
|
|
||||||
// Verify indicates whether to verify client cert.
|
|
||||||
Verify bool `yaml:"verify"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListenerConfig struct {
|
|
||||||
Address string `yaml:"address"`
|
|
||||||
*TLSOptions `yaml:"tls"`
|
|
||||||
Websocket *WebsocketOptions `yaml:"websocket"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type WebsocketOptions struct {
|
|
||||||
Path string `yaml:"path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
type config Config
|
|
||||||
raw := config(DefaultConfig())
|
|
||||||
if err := unmarshal(&raw); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
emptyMQTT := MQTT{}
|
|
||||||
if raw.MQTT == emptyMQTT {
|
|
||||||
raw.MQTT = DefaultMQTTConfig
|
|
||||||
}
|
|
||||||
if len(raw.Plugins) == 0 {
|
|
||||||
raw.Plugins = make(pluginConfig)
|
|
||||||
for name, v := range defaultPluginConfig {
|
|
||||||
raw.Plugins[name] = v
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for name, v := range raw.Plugins {
|
|
||||||
if v == nil {
|
|
||||||
raw.Plugins[name] = defaultPluginConfig[name]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*c = Config(raw)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Config) Validate() (err error) {
|
|
||||||
err = c.Log.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.API.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.MQTT.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.Persistence.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, conf := range c.Plugins {
|
|
||||||
err := conf.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseConfig(filePath string) (Config, error) {
|
|
||||||
if filePath == "" {
|
|
||||||
return DefaultConfig(), nil
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(filePath); err != nil {
|
|
||||||
fmt.Println("unspecificed configuration file, use default config")
|
|
||||||
return DefaultConfig(), nil
|
|
||||||
}
|
|
||||||
b, err := ioutil.ReadFile(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return config, err
|
|
||||||
}
|
|
||||||
config = DefaultConfig()
|
|
||||||
err = yaml.Unmarshal(b, &config)
|
|
||||||
if err != nil {
|
|
||||||
return config, err
|
|
||||||
}
|
|
||||||
config.ConfigDir = path.Dir(filePath)
|
|
||||||
err = config.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return Config{}, err
|
|
||||||
}
|
|
||||||
configFileFullPath = filePath
|
|
||||||
return config, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateLogLevel(level string) {
|
|
||||||
config.Log.Level = level
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLogLevel() string {
|
|
||||||
return config.Log.Level
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteToFile() error {
|
|
||||||
return config.writeToFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Config) writeToFile() error {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
buff bytes.Buffer
|
|
||||||
)
|
|
||||||
e := yaml.NewEncoder(&buff)
|
|
||||||
if err = e.Encode(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = ioutil.WriteFile(configFileFullPath+".tmp", buff.Bytes(), 0644); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
os.Remove(configFileFullPath)
|
|
||||||
return os.Rename(configFileFullPath+".tmp", configFileFullPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Config) GetLogger(config LogConfig) (*zap.AtomicLevel, *zap.Logger, error) {
|
|
||||||
var logLevel zapcore.Level
|
|
||||||
err := logLevel.UnmarshalText([]byte(config.Level))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
var level = zap.NewAtomicLevelAt(logLevel)
|
|
||||||
if config.FilePath == "" {
|
|
||||||
cfg := zap.NewDevelopmentConfig()
|
|
||||||
cfg.Level = level
|
|
||||||
cfg.EncoderConfig.ConsoleSeparator = " "
|
|
||||||
cfg.EncoderConfig.LineEnding = zapcore.DefaultLineEnding
|
|
||||||
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05.000")
|
|
||||||
cfg.EncoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
|
|
||||||
cfg.EncoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
|
|
||||||
cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
|
||||||
logger, err := cfg.Build(zap.AddStacktrace(zapcore.PanicLevel))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return &level, logger.Named(EdgeMqttBroker), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSyncer := getLogWriter(config)
|
|
||||||
encoder := getEncoder()
|
|
||||||
core := zapcore.NewCore(encoder, writeSyncer, level.Level())
|
|
||||||
logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.PanicLevel))
|
|
||||||
|
|
||||||
return &level, logger.Named(EdgeMqttBroker), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEncoder() zapcore.Encoder {
|
|
||||||
encoderConfig := zap.NewProductionEncoderConfig()
|
|
||||||
encoderConfig.LineEnding = zapcore.DefaultLineEnding
|
|
||||||
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05.000")
|
|
||||||
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
|
||||||
encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
|
|
||||||
encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
|
|
||||||
encoderConfig.ConsoleSeparator = " "
|
|
||||||
return zapcore.NewConsoleEncoder(encoderConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLogWriter(cfg LogConfig) zapcore.WriteSyncer {
|
|
||||||
lumberJackLogger := &lumberjack.Logger{
|
|
||||||
Filename: cfg.FilePath,
|
|
||||||
MaxSize: 10,
|
|
||||||
MaxBackups: 3,
|
|
||||||
MaxAge: 7,
|
|
||||||
Compress: false,
|
|
||||||
}
|
|
||||||
return zapcore.AddSync(lumberJackLogger)
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// Code generated by config. DO NOT EDIT.
|
|
||||||
// Source: config/config.go
|
|
||||||
|
|
||||||
// Package config is a generated GoMock package.
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockConfiguration is a mock of Configuration interface
|
|
||||||
type MockConfiguration struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockConfigurationMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockConfigurationMockRecorder is the mock recorder for MockConfiguration
|
|
||||||
type MockConfigurationMockRecorder struct {
|
|
||||||
mock *MockConfiguration
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockConfiguration creates a new mock instance
|
|
||||||
func NewMockConfiguration(ctrl *gomock.Controller) *MockConfiguration {
|
|
||||||
mock := &MockConfiguration{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockConfigurationMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *MockConfiguration) EXPECT() *MockConfigurationMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate mocks base method
|
|
||||||
func (m *MockConfiguration) Validate() error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Validate")
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate indicates an expected call of Validate
|
|
||||||
func (mr *MockConfigurationMockRecorder) Validate() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockConfiguration)(nil).Validate))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML mocks base method
|
|
||||||
func (m *MockConfiguration) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "UnmarshalYAML", unmarshal)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML indicates an expected call of UnmarshalYAML
|
|
||||||
func (mr *MockConfigurationMockRecorder) UnmarshalYAML(unmarshal interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmarshalYAML", reflect.TypeOf((*MockConfiguration)(nil).UnmarshalYAML), unmarshal)
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseConfig(t *testing.T) {
|
|
||||||
var tt = []struct {
|
|
||||||
caseName string
|
|
||||||
fileName string
|
|
||||||
hasErr bool
|
|
||||||
expected Config
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
caseName: "defaultConfig",
|
|
||||||
fileName: "",
|
|
||||||
hasErr: false,
|
|
||||||
expected: DefaultConfig(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range tt {
|
|
||||||
t.Run(v.caseName, func(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
c, err := ParseConfig(v.fileName)
|
|
||||||
if v.hasErr {
|
|
||||||
a.NotNil(err)
|
|
||||||
} else {
|
|
||||||
a.Nil(err)
|
|
||||||
}
|
|
||||||
a.Equal(v.expected, c)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Overlap = "overlap"
|
|
||||||
OnlyOnce = "onlyonce"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultMQTTConfig
|
|
||||||
DefaultMQTTConfig = MQTT{
|
|
||||||
SessionExpiry: 2 * time.Hour,
|
|
||||||
SessionExpiryCheckInterval: 20 * time.Second,
|
|
||||||
MessageExpiry: 2 * time.Hour,
|
|
||||||
InflightExpiry: 30 * time.Second,
|
|
||||||
MaxPacketSize: packets.MaximumSize,
|
|
||||||
ReceiveMax: 100,
|
|
||||||
MaxKeepAlive: 300,
|
|
||||||
TopicAliasMax: 10,
|
|
||||||
SubscriptionIDAvailable: true,
|
|
||||||
SharedSubAvailable: true,
|
|
||||||
WildcardAvailable: true,
|
|
||||||
RetainAvailable: true,
|
|
||||||
MaxQueuedMsg: 1000,
|
|
||||||
MaxInflight: 100,
|
|
||||||
MaximumQoS: 2,
|
|
||||||
QueueQos0Msg: true,
|
|
||||||
DeliveryMode: OnlyOnce,
|
|
||||||
AllowZeroLenClientID: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type MQTT struct {
|
|
||||||
// SessionExpiry is the maximum session expiry interval in seconds.
|
|
||||||
SessionExpiry time.Duration `yaml:"session_expiry"`
|
|
||||||
// SessionExpiryCheckInterval is the interval time for session expiry checker to check whether there
|
|
||||||
// are expired sessions.
|
|
||||||
SessionExpiryCheckInterval time.Duration `yaml:"session_expiry_check_interval"`
|
|
||||||
// MessageExpiry is the maximum lifetime of the message in seconds.
|
|
||||||
// If a message in the queue is not sent in MessageExpiry time, it will be removed, which means it will not be sent to the subscriber.
|
|
||||||
MessageExpiry time.Duration `yaml:"message_expiry"`
|
|
||||||
// InflightExpiry is the lifetime of the "inflight" message in seconds.
|
|
||||||
// If a "inflight" message is not acknowledged by a client in InflightExpiry time, it will be removed when the message queue is full.
|
|
||||||
InflightExpiry time.Duration `yaml:"inflight_expiry"`
|
|
||||||
// MaxPacketSize is the maximum packet size that the server is willing to accept from the client
|
|
||||||
MaxPacketSize uint32 `yaml:"max_packet_size"`
|
|
||||||
// ReceiveMax limits the number of QoS 1 and QoS 2 publications that the server is willing to process concurrently for the client.
|
|
||||||
ReceiveMax uint16 `yaml:"server_receive_maximum"`
|
|
||||||
// MaxKeepAlive is the maximum keep alive time in seconds allows by the server.
|
|
||||||
// If the client requests a keepalive time bigger than MaxKeepalive,
|
|
||||||
// the server will use MaxKeepAlive as the keepalive time.
|
|
||||||
// In this case, if the client version is v5, the server will set MaxKeepalive into CONNACK to inform the client.
|
|
||||||
// But if the client version is 3.x, the server has no way to inform the client that the keepalive time has been changed.
|
|
||||||
MaxKeepAlive uint16 `yaml:"max_keepalive"`
|
|
||||||
// TopicAliasMax indicates the highest value that the server will accept as a Topic Alias sent by the client.
|
|
||||||
// No-op if the client version is MQTTv3.x
|
|
||||||
TopicAliasMax uint16 `yaml:"topic_alias_maximum"`
|
|
||||||
// SubscriptionIDAvailable indicates whether the server supports Subscription Identifiers.
|
|
||||||
// No-op if the client version is MQTTv3.x .
|
|
||||||
SubscriptionIDAvailable bool `yaml:"subscription_identifier_available"`
|
|
||||||
// SharedSubAvailable indicates whether the server supports Shared Subscriptions.
|
|
||||||
SharedSubAvailable bool `yaml:"shared_subscription_available"`
|
|
||||||
// WildcardSubAvailable indicates whether the server supports Wildcard Subscriptions.
|
|
||||||
WildcardAvailable bool `yaml:"wildcard_subscription_available"`
|
|
||||||
// RetainAvailable indicates whether the server supports retained messages.
|
|
||||||
RetainAvailable bool `yaml:"retain_available"`
|
|
||||||
// MaxQueuedMsg is the maximum queue length of the outgoing messages.
|
|
||||||
// If the queue is full, some message will be dropped.
|
|
||||||
// The message dropping strategy is described in the document of the persistence/queue.Store interface.
|
|
||||||
MaxQueuedMsg int `yaml:"max_queued_messages"`
|
|
||||||
// MaxInflight limits inflight message length of the outgoing messages.
|
|
||||||
// Inflight message is also stored in the message queue, so it must be less than or equal to MaxQueuedMsg.
|
|
||||||
// Inflight message is the QoS 1 or QoS 2 message that has been sent out to a client but not been acknowledged yet.
|
|
||||||
MaxInflight uint16 `yaml:"max_inflight"`
|
|
||||||
// MaximumQoS is the highest QOS level permitted for a Publish.
|
|
||||||
MaximumQoS uint8 `yaml:"maximum_qos"`
|
|
||||||
// QueueQos0Msg indicates whether to store QoS 0 message for a offline session.
|
|
||||||
QueueQos0Msg bool `yaml:"queue_qos0_messages"`
|
|
||||||
// DeliveryMode is the delivery mode. The possible value can be "overlap" or "onlyonce".
|
|
||||||
// It is possible for a client’s subscriptions to overlap so that a published message might match multiple filters.
|
|
||||||
// When set to "overlap" , the server will deliver one message for each matching subscription and respecting the subscription’s QoS in each case.
|
|
||||||
// When set to "onlyonce",the server will deliver the message to the client respecting the maximum QoS of all the matching subscriptions.
|
|
||||||
DeliveryMode string `yaml:"delivery_mode"`
|
|
||||||
// AllowZeroLenClientID indicates whether to allow a client to connect with empty client id.
|
|
||||||
AllowZeroLenClientID bool `yaml:"allow_zero_length_clientid"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c MQTT) Validate() error {
|
|
||||||
if c.MaximumQoS > packets.Qos2 {
|
|
||||||
return fmt.Errorf("invalid maximum_qos: %d", c.MaximumQoS)
|
|
||||||
}
|
|
||||||
if c.MaxQueuedMsg <= 0 {
|
|
||||||
return fmt.Errorf("invalid max_queued_messages : %d", c.MaxQueuedMsg)
|
|
||||||
}
|
|
||||||
if c.ReceiveMax == 0 {
|
|
||||||
return fmt.Errorf("server_receive_maximum cannot be 0")
|
|
||||||
}
|
|
||||||
if c.MaxPacketSize == 0 {
|
|
||||||
return fmt.Errorf("max_packet_size cannot be 0")
|
|
||||||
}
|
|
||||||
if c.MaxInflight == 0 {
|
|
||||||
return fmt.Errorf("max_inflight cannot be 0")
|
|
||||||
}
|
|
||||||
if c.DeliveryMode != Overlap && c.DeliveryMode != OnlyOnce {
|
|
||||||
return fmt.Errorf("invalid delivery_mode: %s", c.DeliveryMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.MaxQueuedMsg < int(c.MaxInflight) {
|
|
||||||
return fmt.Errorf("max_queued_message cannot be less than max_inflight")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PersistenceType = string
|
|
||||||
|
|
||||||
const (
|
|
||||||
PersistenceTypeMemory PersistenceType = "memory"
|
|
||||||
PersistenceTypeRedis PersistenceType = "redis"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultMaxActive = uint(0)
|
|
||||||
defaultMaxIdle = uint(1000)
|
|
||||||
// DefaultPersistenceConfig is the default value of Persistence
|
|
||||||
DefaultPersistenceConfig = Persistence{
|
|
||||||
Type: PersistenceTypeMemory,
|
|
||||||
Redis: RedisPersistence{
|
|
||||||
Addr: "127.0.0.1:6379",
|
|
||||||
Password: "",
|
|
||||||
Database: 0,
|
|
||||||
MaxIdle: &defaultMaxIdle,
|
|
||||||
MaxActive: &defaultMaxActive,
|
|
||||||
IdleTimeout: 240 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Persistence is the config of backend persistence.
|
|
||||||
type Persistence struct {
|
|
||||||
// Type is the persistence type.
|
|
||||||
// If empty, use "memory" as default.
|
|
||||||
Type PersistenceType `yaml:"type"`
|
|
||||||
// Redis is the redis configuration and must be set when Type == "redis".
|
|
||||||
Redis RedisPersistence `yaml:"redis"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RedisPersistence is the configuration of redis persistence.
|
|
||||||
type RedisPersistence struct {
|
|
||||||
// Addr is the redis server address.
|
|
||||||
// If empty, use "127.0.0.1:6379" as default.
|
|
||||||
Addr string `yaml:"addr"`
|
|
||||||
// Password is the redis password.
|
|
||||||
Password string `yaml:"password"`
|
|
||||||
// Database is the number of the redis database to be connected.
|
|
||||||
Database uint `yaml:"database"`
|
|
||||||
// MaxIdle is the maximum number of idle connections in the pool.
|
|
||||||
// If nil, use 1000 as default.
|
|
||||||
// This value will pass to redis.Pool.MaxIde.
|
|
||||||
MaxIdle *uint `yaml:"max_idle"`
|
|
||||||
// MaxActive is the maximum number of connections allocated by the pool at a given time.
|
|
||||||
// If nil, use 0 as default.
|
|
||||||
// If zero, there is no limit on the number of connections in the pool.
|
|
||||||
// This value will pass to redis.Pool.MaxActive.
|
|
||||||
MaxActive *uint `yaml:"max_active"`
|
|
||||||
// Close connections after remaining idle for this duration. If the value
|
|
||||||
// is zero, then idle connections are not closed. Applications should set
|
|
||||||
// the timeout to a value less than the server's timeout.
|
|
||||||
// Ff zero, use 240 * time.Second as default.
|
|
||||||
// This value will pass to redis.Pool.IdleTimeout.
|
|
||||||
IdleTimeout time.Duration `yaml:"idle_timeout"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Persistence) Validate() error {
|
|
||||||
if p.Type != PersistenceTypeMemory && p.Type != PersistenceTypeRedis {
|
|
||||||
return errors.New("invalid persistence type")
|
|
||||||
}
|
|
||||||
_, _, err := net.SplitHostPort(p.Redis.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if p.Redis.Database < 0 {
|
|
||||||
return errors.New("invalid redis database number")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
listeners:
|
|
||||||
- address: ":58090"
|
|
||||||
websocket:
|
|
||||||
path: "/"
|
|
||||||
- address: ":1234"
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
session_expiry: 1m
|
|
||||||
message_expiry: 1m
|
|
||||||
max_packet_size: 200
|
|
||||||
server_receive_maximum: 65535
|
|
||||||
max_keepalive: 0 # unlimited
|
|
||||||
topic_alias_maximum: 0 # 0 means not Supported
|
|
||||||
subscription_identifier_available: true
|
|
||||||
wildcard_subscription_available: true
|
|
||||||
shared_subscription_available: true
|
|
||||||
maximum_qos: 2
|
|
||||||
retain_available: true
|
|
||||||
max_queued_messages: 1000
|
|
||||||
max_inflight: 32
|
|
||||||
max_awaiting_rel: 100
|
|
||||||
queue_qos0_messages: true
|
|
||||||
delivery_mode: overlap # overlap or onlyonce
|
|
||||||
allow_zero_length_clientid: true
|
|
||||||
|
|
||||||
log:
|
|
||||||
level: debug # debug | info | warning | error
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
listeners:
|
|
||||||
mqtt:
|
|
||||||
log:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
type TopicAliasType = string
|
|
||||||
|
|
||||||
const (
|
|
||||||
TopicAliasMgrTypeFIFO TopicAliasType = "fifo"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultTopicAliasManager is the default value of TopicAliasManager
|
|
||||||
DefaultTopicAliasManager = TopicAliasManager{
|
|
||||||
Type: TopicAliasMgrTypeFIFO,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// TopicAliasManager is the config of the topic alias manager.
|
|
||||||
type TopicAliasManager struct {
|
|
||||||
Type TopicAliasType
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright 2017 Dell Inc.
|
|
||||||
* Copyright (c) 2019 Intel Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
||||||
* in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
||||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
||||||
* or implied. See the License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*******************************************************************************/
|
|
||||||
package mqttbroker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/winc-link/hummingbird/internal/dtos"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/infrastructure/sqlite"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/interfaces"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
DbClient interfaces.DBClient
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetDbClient() interfaces.DBClient {
|
|
||||||
return DbClient
|
|
||||||
}
|
|
||||||
|
|
||||||
type Database struct {
|
|
||||||
conf config.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDatabase is a factory method that returns an initialized Database receiver struct.
|
|
||||||
func NewDatabase(conf config.Config) Database {
|
|
||||||
return Database{
|
|
||||||
conf: conf,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// init the dbClient interfaces
|
|
||||||
func (d Database) InitDBClient(
|
|
||||||
lc *zap.Logger) error {
|
|
||||||
dbClient, err := sqlite.NewClient(dtos.Configuration{
|
|
||||||
Cluster: d.conf.Database.Cluster,
|
|
||||||
Username: d.conf.Database.Username,
|
|
||||||
Password: d.conf.Database.Password,
|
|
||||||
DataSource: d.conf.Database.DataSource,
|
|
||||||
DatabaseName: d.conf.Database.Name,
|
|
||||||
}, lc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
DbClient = dbClient
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright 2017 Dell Inc.
|
|
||||||
* Copyright (c) 2019 Intel Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
||||||
* in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
||||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
||||||
* or implied. See the License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*******************************************************************************/
|
|
||||||
package sqlite
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/winc-link/hummingbird/internal/dtos"
|
|
||||||
"github.com/winc-link/hummingbird/internal/models"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/errort"
|
|
||||||
clientSQLite "github.com/winc-link/hummingbird/internal/tools/sqldb/sqlite"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
Pool *gorm.DB
|
|
||||||
client clientSQLite.ClientSQLite
|
|
||||||
loggingClient *zap.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClient(config dtos.Configuration, lc *zap.Logger) (c *Client, errEdgeX error) {
|
|
||||||
client, err := clientSQLite.NewGormClient(config, nil)
|
|
||||||
if err != nil {
|
|
||||||
errEdgeX = errort.NewCommonEdgeX(errort.DefaultSystemError, "database failed to init", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
client.Pool = client.Pool.Debug()
|
|
||||||
// 自动建表
|
|
||||||
if err = client.InitTable(
|
|
||||||
&models.MqttAuth{},
|
|
||||||
); err != nil {
|
|
||||||
errEdgeX = errort.NewCommonEdgeX(errort.DefaultSystemError, "database failed to init", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c = &Client{
|
|
||||||
client: client,
|
|
||||||
loggingClient: lc,
|
|
||||||
Pool: client.Pool,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) CloseSession() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) GetMqttAutInfo(clientId string) (models.MqttAuth, error) {
|
|
||||||
return getMqttAutInfo(client, clientId)
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright 2017 Dell Inc.
|
|
||||||
* Copyright (c) 2019 Intel Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
||||||
* in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
||||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
||||||
* or implied. See the License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*******************************************************************************/
|
|
||||||
package sqlite
|
|
||||||
|
|
||||||
import "github.com/winc-link/hummingbird/internal/models"
|
|
||||||
|
|
||||||
func getMqttAutInfo(c *Client, clientId string) (models.MqttAuth, error) {
|
|
||||||
var mqttAuth models.MqttAuth
|
|
||||||
|
|
||||||
if err := c.Pool.Where("client_id = ?", clientId).First(&mqttAuth).Error; err != nil {
|
|
||||||
return models.MqttAuth{}, err
|
|
||||||
}
|
|
||||||
return mqttAuth, nil
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright 2017 Dell Inc.
|
|
||||||
* Copyright (c) 2019 Intel Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
||||||
* in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
||||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
||||||
* or implied. See the License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*******************************************************************************/
|
|
||||||
package interfaces
|
|
||||||
|
|
||||||
import "github.com/winc-link/hummingbird/internal/models"
|
|
||||||
|
|
||||||
type DBClient interface {
|
|
||||||
CloseSession()
|
|
||||||
|
|
||||||
GetMqttAutInfo(clientId string) (models.MqttAuth, error)
|
|
||||||
}
|
|
@ -1,191 +0,0 @@
|
|||||||
package mqttbroker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
Dup bool
|
|
||||||
QoS uint8
|
|
||||||
Retained bool
|
|
||||||
Topic string
|
|
||||||
Payload []byte
|
|
||||||
PacketID packets.PacketID
|
|
||||||
// The following fields are introduced in v5 specification.
|
|
||||||
// Excepting MessageExpiry, these fields will not take effect when it represents a v3.x publish packet.
|
|
||||||
ContentType string
|
|
||||||
CorrelationData []byte
|
|
||||||
MessageExpiry uint32
|
|
||||||
PayloadFormat packets.PayloadFormat
|
|
||||||
ResponseTopic string
|
|
||||||
SubscriptionIdentifier []uint32
|
|
||||||
UserProperties []packets.UserProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy deep copies the Message and return the new one
|
|
||||||
func (m *Message) Copy() *Message {
|
|
||||||
newMsg := &Message{
|
|
||||||
Dup: m.Dup,
|
|
||||||
QoS: m.QoS,
|
|
||||||
Retained: m.Retained,
|
|
||||||
Topic: m.Topic,
|
|
||||||
PacketID: m.PacketID,
|
|
||||||
ContentType: m.ContentType,
|
|
||||||
MessageExpiry: m.MessageExpiry,
|
|
||||||
PayloadFormat: m.PayloadFormat,
|
|
||||||
ResponseTopic: m.ResponseTopic,
|
|
||||||
}
|
|
||||||
newMsg.Payload = make([]byte, len(m.Payload))
|
|
||||||
copy(newMsg.Payload, m.Payload)
|
|
||||||
|
|
||||||
if len(m.CorrelationData) != 0 {
|
|
||||||
newMsg.CorrelationData = make([]byte, len(m.CorrelationData))
|
|
||||||
copy(newMsg.CorrelationData, m.CorrelationData)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(m.SubscriptionIdentifier) != 0 {
|
|
||||||
newMsg.SubscriptionIdentifier = make([]uint32, len(m.SubscriptionIdentifier))
|
|
||||||
copy(newMsg.SubscriptionIdentifier, m.SubscriptionIdentifier)
|
|
||||||
}
|
|
||||||
if len(m.UserProperties) != 0 {
|
|
||||||
newMsg.UserProperties = make([]packets.UserProperty, len(m.UserProperties))
|
|
||||||
for k := range newMsg.UserProperties {
|
|
||||||
newMsg.UserProperties[k].K = make([]byte, len(m.UserProperties[k].K))
|
|
||||||
copy(newMsg.UserProperties[k].K, m.UserProperties[k].K)
|
|
||||||
|
|
||||||
newMsg.UserProperties[k].V = make([]byte, len(m.UserProperties[k].V))
|
|
||||||
copy(newMsg.UserProperties[k].V, m.UserProperties[k].V)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newMsg
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func getVariablelenght(l int) int {
|
|
||||||
if l <= 127 {
|
|
||||||
return 1
|
|
||||||
} else if l <= 16383 {
|
|
||||||
return 2
|
|
||||||
} else if l <= 2097151 {
|
|
||||||
return 3
|
|
||||||
} else if l <= 268435455 {
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// TotalBytes return the publish packets total bytes.
|
|
||||||
func (m *Message) TotalBytes(version packets.Version) uint32 {
|
|
||||||
remainLenght := len(m.Payload) + 2 + len(m.Topic)
|
|
||||||
if m.QoS > packets.Qos0 {
|
|
||||||
remainLenght += 2
|
|
||||||
}
|
|
||||||
if version == packets.Version5 {
|
|
||||||
propertyLenght := 0
|
|
||||||
if m.PayloadFormat == packets.PayloadFormatString {
|
|
||||||
propertyLenght += 2
|
|
||||||
}
|
|
||||||
if l := len(m.ContentType); l != 0 {
|
|
||||||
propertyLenght += 3 + l
|
|
||||||
}
|
|
||||||
if l := len(m.CorrelationData); l != 0 {
|
|
||||||
propertyLenght += 3 + l
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range m.SubscriptionIdentifier {
|
|
||||||
propertyLenght++
|
|
||||||
propertyLenght += getVariablelenght(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.MessageExpiry != 0 {
|
|
||||||
propertyLenght += 5
|
|
||||||
}
|
|
||||||
if l := len(m.ResponseTopic); l != 0 {
|
|
||||||
propertyLenght += 3 + l
|
|
||||||
}
|
|
||||||
for _, v := range m.UserProperties {
|
|
||||||
propertyLenght += 5 + len(v.K) + len(v.V)
|
|
||||||
}
|
|
||||||
remainLenght += propertyLenght + getVariablelenght(propertyLenght)
|
|
||||||
}
|
|
||||||
if remainLenght <= 127 {
|
|
||||||
return 2 + uint32(remainLenght)
|
|
||||||
} else if remainLenght <= 16383 {
|
|
||||||
return 3 + uint32(remainLenght)
|
|
||||||
} else if remainLenght <= 2097151 {
|
|
||||||
return 4 + uint32(remainLenght)
|
|
||||||
}
|
|
||||||
return 5 + uint32(remainLenght)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageFromPublish create the Message instance from publish packets
|
|
||||||
func MessageFromPublish(p *packets.Publish) *Message {
|
|
||||||
m := &Message{
|
|
||||||
Dup: p.Dup,
|
|
||||||
QoS: p.Qos,
|
|
||||||
Retained: p.Retain,
|
|
||||||
Topic: string(p.TopicName),
|
|
||||||
Payload: p.Payload,
|
|
||||||
}
|
|
||||||
if p.Version == packets.Version5 {
|
|
||||||
if p.Properties.PayloadFormat != nil {
|
|
||||||
m.PayloadFormat = *p.Properties.PayloadFormat
|
|
||||||
}
|
|
||||||
if l := len(p.Properties.ContentType); l != 0 {
|
|
||||||
m.ContentType = string(p.Properties.ContentType)
|
|
||||||
}
|
|
||||||
if l := len(p.Properties.CorrelationData); l != 0 {
|
|
||||||
m.CorrelationData = p.Properties.CorrelationData
|
|
||||||
}
|
|
||||||
if p.Properties.MessageExpiry != nil {
|
|
||||||
m.MessageExpiry = *p.Properties.MessageExpiry
|
|
||||||
}
|
|
||||||
if l := len(p.Properties.ResponseTopic); l != 0 {
|
|
||||||
m.ResponseTopic = string(p.Properties.ResponseTopic)
|
|
||||||
}
|
|
||||||
m.UserProperties = p.Properties.User
|
|
||||||
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageToPublish create the publish packet instance from *Message
|
|
||||||
func MessageToPublish(msg *Message, version packets.Version) *packets.Publish {
|
|
||||||
pub := &packets.Publish{
|
|
||||||
Dup: msg.Dup,
|
|
||||||
Qos: msg.QoS,
|
|
||||||
PacketID: msg.PacketID,
|
|
||||||
Retain: msg.Retained,
|
|
||||||
TopicName: []byte(msg.Topic),
|
|
||||||
Payload: msg.Payload,
|
|
||||||
Version: version,
|
|
||||||
}
|
|
||||||
if version == packets.Version5 {
|
|
||||||
var msgExpiry *uint32
|
|
||||||
if e := msg.MessageExpiry; e != 0 {
|
|
||||||
msgExpiry = &e
|
|
||||||
}
|
|
||||||
var contentType []byte
|
|
||||||
if msg.ContentType != "" {
|
|
||||||
contentType = []byte(msg.ContentType)
|
|
||||||
}
|
|
||||||
var responseTopic []byte
|
|
||||||
if msg.ResponseTopic != "" {
|
|
||||||
responseTopic = []byte(msg.ResponseTopic)
|
|
||||||
}
|
|
||||||
var payloadFormat *byte
|
|
||||||
if e := msg.PayloadFormat; e == packets.PayloadFormatString {
|
|
||||||
payloadFormat = &e
|
|
||||||
}
|
|
||||||
pub.Properties = &packets.Properties{
|
|
||||||
CorrelationData: msg.CorrelationData,
|
|
||||||
ContentType: contentType,
|
|
||||||
MessageExpiry: msgExpiry,
|
|
||||||
ResponseTopic: responseTopic,
|
|
||||||
PayloadFormat: payloadFormat,
|
|
||||||
User: msg.UserProperties,
|
|
||||||
SubscriptionIdentifier: msg.SubscriptionIdentifier,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pub
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
mockgen -source=config/config.go -destination=./config/config_mock.go -package=config -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/config
|
|
||||||
mockgen -source=persistence/queue/elem.go -destination=./persistence/queue/elem_mock.go -package=queue -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/queue
|
|
||||||
mockgen -source=persistence/queue/queue.go -destination=./persistence/queue/queue_mock.go -package=queue -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/queue
|
|
||||||
mockgen -source=persistence/session/session.go -destination=./persistence/session/session_mock.go -package=session -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/session
|
|
||||||
mockgen -source=persistence/subscription/subscription.go -destination=./persistence/subscription/subscription_mock.go -package=subscription -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/subscription
|
|
||||||
mockgen -source=persistence/unack/unack.go -destination=./persistence/unack/unack_mock.go -package=unack -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/unack
|
|
||||||
mockgen -source=pkg/packets/packets.go -destination=./pkg/packets/packets_mock.go -package=packets -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/packets
|
|
||||||
mockgen -source=plugin/auth/account_grpc.pb.go -destination=./plugin/auth/account_grpc.pb_mock.go -package=auth -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/auth
|
|
||||||
mockgen -source=plugin/federation/federation.pb.go -destination=./plugin/federation/federation.pb_mock.go -package=federation -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/federation
|
|
||||||
mockgen -source=plugin/federation/peer.go -destination=./plugin/federation/peer_mock.go -package=federation -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/federation
|
|
||||||
mockgen -source=plugin/federation/membership.go -destination=./plugin/federation/membership_mock.go -package=federation -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/federation
|
|
||||||
mockgen -source=retained/interface.go -destination=./retained/interface_mock.go -package=retained -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/retained
|
|
||||||
mockgen -source=server/client.go -destination=./server/client_mock.go -package=server -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/server
|
|
||||||
mockgen -source=server/persistence.go -destination=./server/persistence_mock.go -package=server -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/server
|
|
||||||
mockgen -source=server/plugin.go -destination=./server/plugin_mock.go -package=server -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/server
|
|
||||||
mockgen -source=server/server.go -destination=./server/server_mock.go -package=server -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/server
|
|
||||||
mockgen -source=server/service.go -destination=./server/service_mock.go -package=server -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/server
|
|
||||||
mockgen -source=server/stats.go -destination=./server/stats_mock.go -package=server -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/server
|
|
||||||
mockgen -source=server/topic_alias.go -destination=./server/topic_alias_mock.go -package=server -self_package=gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/server
|
|
||||||
|
|
||||||
# reflection mode.
|
|
||||||
# gRPC streaming mock issue: https://github.com/golang/mock/pull/163
|
|
||||||
mockgen -package=federation -destination=/usr/local/gopath/src/gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/plugin/federation/federation_grpc.pb_mock.go gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/plugin/federation FederationClient,Federation_EventStreamClient
|
|
@ -1,73 +0,0 @@
|
|||||||
package encoding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WriteUint16(w *bytes.Buffer, i uint16) {
|
|
||||||
w.WriteByte(byte(i >> 8))
|
|
||||||
w.WriteByte(byte(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteBool(w *bytes.Buffer, b bool) {
|
|
||||||
if b {
|
|
||||||
w.WriteByte(1)
|
|
||||||
} else {
|
|
||||||
w.WriteByte(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadBool(r *bytes.Buffer) (bool, error) {
|
|
||||||
b, err := r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if b == 0 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteString(w *bytes.Buffer, s []byte) {
|
|
||||||
WriteUint16(w, uint16(len(s)))
|
|
||||||
w.Write(s)
|
|
||||||
}
|
|
||||||
func ReadString(r *bytes.Buffer) (b []byte, err error) {
|
|
||||||
l := make([]byte, 2)
|
|
||||||
_, err = io.ReadFull(r, l)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
length := int(binary.BigEndian.Uint16(l))
|
|
||||||
paylaod := make([]byte, length)
|
|
||||||
|
|
||||||
_, err = io.ReadFull(r, paylaod)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return paylaod, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteUint32(w *bytes.Buffer, i uint32) {
|
|
||||||
w.WriteByte(byte(i >> 24))
|
|
||||||
w.WriteByte(byte(i >> 16))
|
|
||||||
w.WriteByte(byte(i >> 8))
|
|
||||||
w.WriteByte(byte(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadUint16(r *bytes.Buffer) (uint16, error) {
|
|
||||||
if r.Len() < 2 {
|
|
||||||
return 0, errors.New("invalid length")
|
|
||||||
}
|
|
||||||
return binary.BigEndian.Uint16(r.Next(2)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadUint32(r *bytes.Buffer) (uint32, error) {
|
|
||||||
if r.Len() < 4 {
|
|
||||||
return 0, errors.New("invalid length")
|
|
||||||
}
|
|
||||||
return binary.BigEndian.Uint32(r.Next(4)), nil
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
package encoding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EncodeMessage encodes message into bytes and write it to the buffer
|
|
||||||
func EncodeMessage(msg *gmqtt.Message, b *bytes.Buffer) {
|
|
||||||
if msg == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
WriteBool(b, msg.Dup)
|
|
||||||
b.WriteByte(msg.QoS)
|
|
||||||
WriteBool(b, msg.Retained)
|
|
||||||
WriteString(b, []byte(msg.Topic))
|
|
||||||
WriteString(b, []byte(msg.Payload))
|
|
||||||
WriteUint16(b, msg.PacketID)
|
|
||||||
|
|
||||||
if len(msg.ContentType) != 0 {
|
|
||||||
b.WriteByte(packets.PropContentType)
|
|
||||||
WriteString(b, []byte(msg.ContentType))
|
|
||||||
}
|
|
||||||
if len(msg.CorrelationData) != 0 {
|
|
||||||
b.WriteByte(packets.PropCorrelationData)
|
|
||||||
WriteString(b, []byte(msg.CorrelationData))
|
|
||||||
}
|
|
||||||
if msg.MessageExpiry != 0 {
|
|
||||||
b.WriteByte(packets.PropMessageExpiry)
|
|
||||||
WriteUint32(b, msg.MessageExpiry)
|
|
||||||
}
|
|
||||||
b.WriteByte(packets.PropPayloadFormat)
|
|
||||||
b.WriteByte(msg.PayloadFormat)
|
|
||||||
|
|
||||||
if len(msg.ResponseTopic) != 0 {
|
|
||||||
b.WriteByte(packets.PropResponseTopic)
|
|
||||||
WriteString(b, []byte(msg.ResponseTopic))
|
|
||||||
}
|
|
||||||
for _, v := range msg.SubscriptionIdentifier {
|
|
||||||
b.WriteByte(packets.PropSubscriptionIdentifier)
|
|
||||||
l, _ := packets.DecodeRemainLength(int(v))
|
|
||||||
b.Write(l)
|
|
||||||
}
|
|
||||||
for _, v := range msg.UserProperties {
|
|
||||||
b.WriteByte(packets.PropUser)
|
|
||||||
WriteString(b, v.K)
|
|
||||||
WriteString(b, v.V)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeMessage decodes message from buffer.
|
|
||||||
func DecodeMessage(b *bytes.Buffer) (msg *gmqtt.Message, err error) {
|
|
||||||
msg = &gmqtt.Message{}
|
|
||||||
msg.Dup, err = ReadBool(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msg.QoS, err = b.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msg.Retained, err = ReadBool(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
topic, err := ReadString(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msg.Topic = string(topic)
|
|
||||||
msg.Payload, err = ReadString(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msg.PacketID, err = ReadUint16(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
pt, err := b.ReadByte()
|
|
||||||
if err == io.EOF {
|
|
||||||
return msg, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch pt {
|
|
||||||
case packets.PropContentType:
|
|
||||||
v, err := ReadString(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msg.ContentType = string(v)
|
|
||||||
case packets.PropCorrelationData:
|
|
||||||
msg.CorrelationData, err = ReadString(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case packets.PropMessageExpiry:
|
|
||||||
msg.MessageExpiry, err = ReadUint32(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case packets.PropPayloadFormat:
|
|
||||||
msg.PayloadFormat, err = b.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case packets.PropResponseTopic:
|
|
||||||
v, err := ReadString(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msg.ResponseTopic = string(v)
|
|
||||||
case packets.PropSubscriptionIdentifier:
|
|
||||||
si, err := packets.EncodeRemainLength(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msg.SubscriptionIdentifier = append(msg.SubscriptionIdentifier, uint32(si))
|
|
||||||
case packets.PropUser:
|
|
||||||
k, err := ReadString(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
v, err := ReadString(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msg.UserProperties = append(msg.UserProperties, packets.UserProperty{K: k, V: v})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeMessageFromBytes decodes message from bytes.
|
|
||||||
func DecodeMessageFromBytes(b []byte) (msg *gmqtt.Message, err error) {
|
|
||||||
if len(b) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return DecodeMessage(bytes.NewBuffer(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func EncodeSession(sess *gmqtt.Session, b *bytes.Buffer) {
|
|
||||||
WriteString(b, []byte(sess.ClientID))
|
|
||||||
if sess.Will != nil {
|
|
||||||
b.WriteByte(1)
|
|
||||||
EncodeMessage(sess.Will, b)
|
|
||||||
WriteUint32(b, sess.WillDelayInterval)
|
|
||||||
} else {
|
|
||||||
b.WriteByte(0)
|
|
||||||
}
|
|
||||||
time := make([]byte, 8)
|
|
||||||
binary.BigEndian.PutUint64(time, uint64(sess.ConnectedAt.Unix()))
|
|
||||||
WriteUint32(b, sess.ExpiryInterval)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecodeSession(b *bytes.Buffer) (sess *gmqtt.Session, err error) {
|
|
||||||
sess = &gmqtt.Session{}
|
|
||||||
cid, err := ReadString(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sess.ClientID = string(cid)
|
|
||||||
willPresent, err := b.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if willPresent == 1 {
|
|
||||||
sess.Will, err = DecodeMessage(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sess.WillDelayInterval, err = ReadUint32(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t := binary.BigEndian.Uint64(b.Next(8))
|
|
||||||
sess.ConnectedAt = time.Unix(int64(t), 0)
|
|
||||||
sess.ExpiryInterval, err = ReadUint32(b)
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/queue"
|
|
||||||
mem_queue "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/queue/mem"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/session"
|
|
||||||
mem_session "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/session/mem"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription"
|
|
||||||
mem_sub "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription/mem"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/unack"
|
|
||||||
mem_unack "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/unack/mem"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
server.RegisterPersistenceFactory("memory", NewMemory)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMemory(config config.Config) (server.Persistence, error) {
|
|
||||||
return &memory{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type memory struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memory) NewUnackStore(config config.Config, clientID string) (unack.Store, error) {
|
|
||||||
return mem_unack.New(mem_unack.Options{
|
|
||||||
ClientID: clientID,
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memory) NewSessionStore(config config.Config) (session.Store, error) {
|
|
||||||
return mem_session.New(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memory) Open() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *memory) NewQueueStore(config config.Config, defaultNotifier queue.Notifier, clientID string) (queue.Store, error) {
|
|
||||||
return mem_queue.New(mem_queue.Options{
|
|
||||||
MaxQueuedMsg: config.MQTT.MaxQueuedMsg,
|
|
||||||
InflightExpiry: config.MQTT.InflightExpiry,
|
|
||||||
ClientID: clientID,
|
|
||||||
DefaultNotifier: defaultNotifier,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memory) NewSubscriptionStore(config config.Config) (subscription.Store, error) {
|
|
||||||
return mem_sub.NewStore(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memory) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
package queue
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/encoding"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MessageWithID interface {
|
|
||||||
ID() packets.PacketID
|
|
||||||
SetID(id packets.PacketID)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Publish struct {
|
|
||||||
*mqttbroker.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Publish) ID() packets.PacketID {
|
|
||||||
return p.PacketID
|
|
||||||
}
|
|
||||||
func (p *Publish) SetID(id packets.PacketID) {
|
|
||||||
p.PacketID = id
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pubrel struct {
|
|
||||||
PacketID packets.PacketID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pubrel) ID() packets.PacketID {
|
|
||||||
return p.PacketID
|
|
||||||
}
|
|
||||||
func (p *Pubrel) SetID(id packets.PacketID) {
|
|
||||||
p.PacketID = id
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elem represents the element store in the queue.
|
|
||||||
type Elem struct {
|
|
||||||
// At represents the entry time.
|
|
||||||
At time.Time
|
|
||||||
// Expiry represents the expiry time.
|
|
||||||
// Empty means never expire.
|
|
||||||
Expiry time.Time
|
|
||||||
MessageWithID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encodes the publish structure into bytes and write it to the buffer
|
|
||||||
func (p *Publish) Encode(b *bytes.Buffer) {
|
|
||||||
encoding.EncodeMessage(p.Message, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Publish) Decode(b *bytes.Buffer) (err error) {
|
|
||||||
msg, err := encoding.DecodeMessage(b)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.Message = msg
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encode the pubrel structure into bytes.
|
|
||||||
func (p *Pubrel) Encode(b *bytes.Buffer) {
|
|
||||||
encoding.WriteUint16(b, p.PacketID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pubrel) Decode(b *bytes.Buffer) (err error) {
|
|
||||||
p.PacketID, err = encoding.ReadUint16(b)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encode the elem structure into bytes.
|
|
||||||
// Format: 8 byte timestamp | 1 byte identifier| data
|
|
||||||
func (e *Elem) Encode() []byte {
|
|
||||||
b := bytes.NewBuffer(make([]byte, 0, 100))
|
|
||||||
rs := make([]byte, 19)
|
|
||||||
binary.BigEndian.PutUint64(rs[0:9], uint64(e.At.Unix()))
|
|
||||||
binary.BigEndian.PutUint64(rs[9:18], uint64(e.Expiry.Unix()))
|
|
||||||
switch m := e.MessageWithID.(type) {
|
|
||||||
case *Publish:
|
|
||||||
rs[18] = 0
|
|
||||||
b.Write(rs)
|
|
||||||
m.Encode(b)
|
|
||||||
case *Pubrel:
|
|
||||||
rs[18] = 1
|
|
||||||
b.Write(rs)
|
|
||||||
m.Encode(b)
|
|
||||||
}
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Elem) Decode(b []byte) (err error) {
|
|
||||||
if len(b) < 19 {
|
|
||||||
return errors.New("invalid input length")
|
|
||||||
}
|
|
||||||
e.At = time.Unix(int64(binary.BigEndian.Uint64(b[0:9])), 0)
|
|
||||||
e.Expiry = time.Unix(int64(binary.BigEndian.Uint64(b[9:19])), 0)
|
|
||||||
switch b[18] {
|
|
||||||
case 0: // publish
|
|
||||||
p := &Publish{}
|
|
||||||
buf := bytes.NewBuffer(b[19:])
|
|
||||||
err = p.Decode(buf)
|
|
||||||
e.MessageWithID = p
|
|
||||||
case 1: // pubrel
|
|
||||||
p := &Pubrel{}
|
|
||||||
buf := bytes.NewBuffer(b[19:])
|
|
||||||
err = p.Decode(buf)
|
|
||||||
e.MessageWithID = p
|
|
||||||
default:
|
|
||||||
return errors.New("invalid identifier")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: persistence/queue/elem.go
|
|
||||||
|
|
||||||
// Package queue is a generated GoMock package.
|
|
||||||
package queue
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
packets "github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockMessageWithID is a mock of MessageWithID interface
|
|
||||||
type MockMessageWithID struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockMessageWithIDMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockMessageWithIDMockRecorder is the mock recorder for MockMessageWithID
|
|
||||||
type MockMessageWithIDMockRecorder struct {
|
|
||||||
mock *MockMessageWithID
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockMessageWithID creates a new mock instance
|
|
||||||
func NewMockMessageWithID(ctrl *gomock.Controller) *MockMessageWithID {
|
|
||||||
mock := &MockMessageWithID{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockMessageWithIDMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *MockMessageWithID) EXPECT() *MockMessageWithIDMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID mocks base method
|
|
||||||
func (m *MockMessageWithID) ID() packets.PacketID {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "ID")
|
|
||||||
ret0, _ := ret[0].(packets.PacketID)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID indicates an expected call of ID
|
|
||||||
func (mr *MockMessageWithIDMockRecorder) ID() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockMessageWithID)(nil).ID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetID mocks base method
|
|
||||||
func (m *MockMessageWithID) SetID(id packets.PacketID) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "SetID", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetID indicates an expected call of SetID
|
|
||||||
func (mr *MockMessageWithIDMockRecorder) SetID(id interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetID", reflect.TypeOf((*MockMessageWithID)(nil).SetID), id)
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
package queue
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
func assertElemEqual(a *assert.Assertions, expected, actual *Elem) {
|
|
||||||
expected.At = time.Unix(expected.At.Unix(), 0)
|
|
||||||
expected.Expiry = time.Unix(expected.Expiry.Unix(), 0)
|
|
||||||
actual.At = time.Unix(actual.At.Unix(), 0)
|
|
||||||
actual.Expiry = time.Unix(actual.Expiry.Unix(), 0)
|
|
||||||
a.Equal(expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestElem_Encode_Publish(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
e := &Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
MessageWithID: &Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 2,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/mytopic",
|
|
||||||
Payload: []byte("payload"),
|
|
||||||
PacketID: 2,
|
|
||||||
ContentType: "type",
|
|
||||||
CorrelationData: nil,
|
|
||||||
MessageExpiry: 1,
|
|
||||||
PayloadFormat: packets.PayloadFormatString,
|
|
||||||
ResponseTopic: "",
|
|
||||||
SubscriptionIdentifier: []uint32{1, 2},
|
|
||||||
UserProperties: []packets.UserProperty{
|
|
||||||
{
|
|
||||||
K: []byte("1"),
|
|
||||||
V: []byte("2"),
|
|
||||||
}, {
|
|
||||||
K: []byte("3"),
|
|
||||||
V: []byte("4"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rs := e.Encode()
|
|
||||||
de := &Elem{}
|
|
||||||
err := de.Decode(rs)
|
|
||||||
a.Nil(err)
|
|
||||||
assertElemEqual(a, e, de)
|
|
||||||
}
|
|
||||||
func TestElem_Encode_Pubrel(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
e := &Elem{
|
|
||||||
At: time.Unix(time.Now().Unix(), 0),
|
|
||||||
MessageWithID: &Pubrel{
|
|
||||||
PacketID: 2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rs := e.Encode()
|
|
||||||
de := &Elem{}
|
|
||||||
err := de.Decode(rs)
|
|
||||||
a.Nil(err)
|
|
||||||
assertElemEqual(a, e, de)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Encode_Publish(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
e := &Elem{
|
|
||||||
At: time.Unix(time.Now().Unix(), 0),
|
|
||||||
MessageWithID: &Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 2,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/mytopic",
|
|
||||||
Payload: []byte("payload"),
|
|
||||||
PacketID: 2,
|
|
||||||
ContentType: "type",
|
|
||||||
CorrelationData: nil,
|
|
||||||
MessageExpiry: 1,
|
|
||||||
PayloadFormat: packets.PayloadFormatString,
|
|
||||||
ResponseTopic: "",
|
|
||||||
SubscriptionIdentifier: []uint32{1, 2},
|
|
||||||
UserProperties: []packets.UserProperty{
|
|
||||||
{
|
|
||||||
K: []byte("1"),
|
|
||||||
V: []byte("2"),
|
|
||||||
}, {
|
|
||||||
K: []byte("3"),
|
|
||||||
V: []byte("4"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
e.Encode()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package queue
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrClosed = errors.New("queue has been closed")
|
|
||||||
ErrDropExceedsMaxPacketSize = errors.New("maximum packet size exceeded")
|
|
||||||
ErrDropQueueFull = errors.New("the message queue is full")
|
|
||||||
ErrDropExpired = errors.New("the message is expired")
|
|
||||||
ErrDropExpiredInflight = errors.New("the inflight message is expired")
|
|
||||||
)
|
|
||||||
|
|
||||||
// InternalError wraps the error of the backend storage.
|
|
||||||
type InternalError struct {
|
|
||||||
// Err is the error return by the backend storage.
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *InternalError) Error() string {
|
|
||||||
return i.Error()
|
|
||||||
}
|
|
@ -1,278 +0,0 @@
|
|||||||
package mem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/queue"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ queue.Store = (*Queue)(nil)
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
MaxQueuedMsg int
|
|
||||||
InflightExpiry time.Duration
|
|
||||||
ClientID string
|
|
||||||
DefaultNotifier queue.Notifier
|
|
||||||
}
|
|
||||||
|
|
||||||
type Queue struct {
|
|
||||||
cond *sync.Cond
|
|
||||||
clientID string
|
|
||||||
version packets.Version
|
|
||||||
opts *Options
|
|
||||||
readBytesLimit uint32
|
|
||||||
l *list.List
|
|
||||||
// current is the next element to read.
|
|
||||||
current *list.Element
|
|
||||||
inflightDrained bool
|
|
||||||
closed bool
|
|
||||||
// max is the maximum queue length
|
|
||||||
max int
|
|
||||||
log *zap.Logger
|
|
||||||
inflightExpiry time.Duration
|
|
||||||
notifier queue.Notifier
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(opts Options) (*Queue, error) {
|
|
||||||
return &Queue{
|
|
||||||
clientID: opts.ClientID,
|
|
||||||
cond: sync.NewCond(&sync.Mutex{}),
|
|
||||||
l: list.New(),
|
|
||||||
max: opts.MaxQueuedMsg,
|
|
||||||
inflightExpiry: opts.InflightExpiry,
|
|
||||||
notifier: opts.DefaultNotifier,
|
|
||||||
log: server.LoggerWithField(zap.String("queue", "memory")),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Close() error {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
q.closed = true
|
|
||||||
q.cond.Signal()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Init(opts *queue.InitOptions) error {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
q.closed = false
|
|
||||||
q.inflightDrained = false
|
|
||||||
if opts.CleanStart {
|
|
||||||
q.l = list.New()
|
|
||||||
}
|
|
||||||
q.readBytesLimit = opts.ReadBytesLimit
|
|
||||||
q.version = opts.Version
|
|
||||||
q.current = q.l.Front()
|
|
||||||
q.notifier = opts.Notifier
|
|
||||||
q.cond.Signal()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Queue) Clean() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Add(elem *queue.Elem) (err error) {
|
|
||||||
now := time.Now()
|
|
||||||
var dropErr error
|
|
||||||
var dropElem *list.Element
|
|
||||||
var drop bool
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer func() {
|
|
||||||
q.cond.L.Unlock()
|
|
||||||
q.cond.Signal()
|
|
||||||
}()
|
|
||||||
defer func() {
|
|
||||||
if drop {
|
|
||||||
if dropErr == queue.ErrDropExpiredInflight {
|
|
||||||
q.notifier.NotifyInflightAdded(-1)
|
|
||||||
}
|
|
||||||
if dropElem == nil {
|
|
||||||
q.notifier.NotifyDropped(elem, dropErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if dropElem == q.current {
|
|
||||||
q.current = q.current.Next()
|
|
||||||
}
|
|
||||||
q.l.Remove(dropElem)
|
|
||||||
q.notifier.NotifyDropped(dropElem.Value.(*queue.Elem), dropErr)
|
|
||||||
} else {
|
|
||||||
q.notifier.NotifyMsgQueueAdded(1)
|
|
||||||
}
|
|
||||||
e := q.l.PushBack(elem)
|
|
||||||
if q.current == nil {
|
|
||||||
q.current = e
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if q.l.Len() >= q.max {
|
|
||||||
// set default drop error
|
|
||||||
dropErr = queue.ErrDropQueueFull
|
|
||||||
drop = true
|
|
||||||
|
|
||||||
// drop expired inflight message
|
|
||||||
if v := q.l.Front(); v != q.current &&
|
|
||||||
v != nil &&
|
|
||||||
queue.ElemExpiry(now, v.Value.(*queue.Elem)) {
|
|
||||||
dropElem = v
|
|
||||||
dropErr = queue.ErrDropExpiredInflight
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop the current elem if there is no more non-inflight messages.
|
|
||||||
if q.inflightDrained && q.current == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for e := q.current; e != nil; e = e.Next() {
|
|
||||||
pub := e.Value.(*queue.Elem).MessageWithID.(*queue.Publish)
|
|
||||||
// drop expired non-inflight message
|
|
||||||
if pub.ID() == 0 &&
|
|
||||||
queue.ElemExpiry(now, e.Value.(*queue.Elem)) {
|
|
||||||
dropElem = e
|
|
||||||
dropErr = queue.ErrDropExpired
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// drop qos0 message in the queue
|
|
||||||
if pub.ID() == 0 && pub.QoS == packets.Qos0 && dropElem == nil {
|
|
||||||
dropElem = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dropElem != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if elem.MessageWithID.(*queue.Publish).QoS == packets.Qos0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if q.inflightDrained {
|
|
||||||
// drop the front message
|
|
||||||
dropElem = q.current
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// the messages in the queue are all inflight messages, drop the current elem
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Replace(elem *queue.Elem) (replaced bool, err error) {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
unread := q.current
|
|
||||||
for e := q.l.Front(); e != nil && e != unread; e = e.Next() {
|
|
||||||
if e.Value.(*queue.Elem).ID() == elem.ID() {
|
|
||||||
e.Value = elem
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Read(pids []packets.PacketID) (rs []*queue.Elem, err error) {
|
|
||||||
now := time.Now()
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
if !q.inflightDrained {
|
|
||||||
panic("must call ReadInflight to drain all inflight messages before Read")
|
|
||||||
}
|
|
||||||
for (q.l.Len() == 0 || q.current == nil) && !q.closed {
|
|
||||||
q.cond.Wait()
|
|
||||||
}
|
|
||||||
if q.closed {
|
|
||||||
return nil, queue.ErrClosed
|
|
||||||
}
|
|
||||||
length := q.l.Len()
|
|
||||||
if len(pids) < length {
|
|
||||||
length = len(pids)
|
|
||||||
}
|
|
||||||
var msgQueueDelta, inflightDelta int
|
|
||||||
var pflag int
|
|
||||||
for i := 0; i < length && q.current != nil; i++ {
|
|
||||||
v := q.current
|
|
||||||
// remove expired message
|
|
||||||
if queue.ElemExpiry(now, v.Value.(*queue.Elem)) {
|
|
||||||
q.current = q.current.Next()
|
|
||||||
q.notifier.NotifyDropped(v.Value.(*queue.Elem), queue.ErrDropExpired)
|
|
||||||
q.l.Remove(v)
|
|
||||||
msgQueueDelta--
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// remove message which exceeds maximum packet size
|
|
||||||
pub := v.Value.(*queue.Elem).MessageWithID.(*queue.Publish)
|
|
||||||
if size := pub.TotalBytes(q.version); size > q.readBytesLimit {
|
|
||||||
q.current = q.current.Next()
|
|
||||||
q.notifier.NotifyDropped(v.Value.(*queue.Elem), queue.ErrDropExceedsMaxPacketSize)
|
|
||||||
q.l.Remove(v)
|
|
||||||
msgQueueDelta--
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove qos 0 message after read
|
|
||||||
if pub.QoS == 0 {
|
|
||||||
q.current = q.current.Next()
|
|
||||||
q.l.Remove(v)
|
|
||||||
msgQueueDelta--
|
|
||||||
} else {
|
|
||||||
pub.SetID(pids[pflag])
|
|
||||||
// When the message becomes inflight message, update the expiry time.
|
|
||||||
if q.inflightExpiry != 0 {
|
|
||||||
v.Value.(*queue.Elem).Expiry = now.Add(q.inflightExpiry)
|
|
||||||
}
|
|
||||||
pflag++
|
|
||||||
inflightDelta++
|
|
||||||
q.current = q.current.Next()
|
|
||||||
}
|
|
||||||
rs = append(rs, v.Value.(*queue.Elem))
|
|
||||||
}
|
|
||||||
q.notifier.NotifyMsgQueueAdded(msgQueueDelta)
|
|
||||||
q.notifier.NotifyInflightAdded(inflightDelta)
|
|
||||||
return rs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) ReadInflight(maxSize uint) (rs []*queue.Elem, err error) {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
length := q.l.Len()
|
|
||||||
if length == 0 || q.current == nil {
|
|
||||||
q.inflightDrained = true
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if int(maxSize) < length {
|
|
||||||
length = int(maxSize)
|
|
||||||
}
|
|
||||||
for i := 0; i < length && q.current != nil; i++ {
|
|
||||||
if e := q.current.Value.(*queue.Elem); e.ID() != 0 {
|
|
||||||
if q.inflightExpiry != 0 {
|
|
||||||
e.Expiry = time.Now().Add(q.inflightExpiry)
|
|
||||||
}
|
|
||||||
rs = append(rs, e)
|
|
||||||
q.current = q.current.Next()
|
|
||||||
} else {
|
|
||||||
q.inflightDrained = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Remove(pid packets.PacketID) error {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
// Must not remove unread messages.
|
|
||||||
unread := q.current
|
|
||||||
for e := q.l.Front(); e != nil && e != unread; e = e.Next() {
|
|
||||||
if e.Value.(*queue.Elem).ID() == pid {
|
|
||||||
q.l.Remove(e)
|
|
||||||
q.notifier.NotifyMsgQueueAdded(-1)
|
|
||||||
q.notifier.NotifyInflightAdded(-1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package queue
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InitOptions is used to pass some required client information to the queue.Init()
|
|
||||||
type InitOptions struct {
|
|
||||||
// CleanStart is the cleanStart field in the connect packet.
|
|
||||||
CleanStart bool
|
|
||||||
// Version is the client MQTT protocol version.
|
|
||||||
Version packets.Version
|
|
||||||
// ReadBytesLimit indicates the maximum publish size that is allow to read.
|
|
||||||
ReadBytesLimit uint32
|
|
||||||
Notifier Notifier
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store represents a queue store for one client.
|
|
||||||
type Store interface {
|
|
||||||
// Close will be called when the client disconnect.
|
|
||||||
// This method must unblock the Read method.
|
|
||||||
Close() error
|
|
||||||
// Init will be called when the client connect.
|
|
||||||
// If opts.CleanStart set to true, the implementation should remove any associated data in backend store.
|
|
||||||
// If it sets to false, the implementation should be able to retrieve the associated data from backend store.
|
|
||||||
// The opts.version indicates the protocol version of the connected client, it is mainly used to calculate the publish packet size.
|
|
||||||
Init(opts *InitOptions) error
|
|
||||||
Clean() error
|
|
||||||
// Add inserts a elem to the queue.
|
|
||||||
// When the len of queue is reaching the maximum setting, the implementation should drop messages according the following priorities:
|
|
||||||
// 1. Drop the expired inflight message.
|
|
||||||
// 2. Drop the current elem if there is no more non-inflight messages.
|
|
||||||
// 3. Drop expired non-inflight message.
|
|
||||||
// 4. Drop qos0 message.
|
|
||||||
// 5. Drop the front message.
|
|
||||||
// See queue.mem for more details.
|
|
||||||
Add(elem *Elem) error
|
|
||||||
// Replace replaces the PUBLISH with the PUBREL with the same packet id.
|
|
||||||
Replace(elem *Elem) (replaced bool, err error)
|
|
||||||
|
|
||||||
// Read reads a batch of new message (non-inflight) from the store. The qos0 messages will be removed after read.
|
|
||||||
// The size of the batch will be less than or equal to the size of the given packet id list.
|
|
||||||
// The implementation must remove and do not return any :
|
|
||||||
// 1. expired messages
|
|
||||||
// 2. publish message which exceeds the InitOptions.ReadBytesLimit
|
|
||||||
// while reading.
|
|
||||||
// The caller must call ReadInflight first to read all inflight message before calling this method.
|
|
||||||
// Calling this method will be blocked until there are any new messages can be read or the store has been closed.
|
|
||||||
// If the store has been closed, returns nil, ErrClosed.
|
|
||||||
Read(pids []packets.PacketID) ([]*Elem, error)
|
|
||||||
|
|
||||||
// ReadInflight reads at most maxSize inflight messages.
|
|
||||||
// The caller must call this method to read all inflight messages before calling Read method.
|
|
||||||
// Returning 0 length elems means all inflight messages have been read.
|
|
||||||
ReadInflight(maxSize uint) (elems []*Elem, err error)
|
|
||||||
|
|
||||||
// Remove removes the elem for a given id.
|
|
||||||
Remove(pid packets.PacketID) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type Notifier interface {
|
|
||||||
// NotifyDropped will be called when the element in the queue is dropped.
|
|
||||||
// The err indicates the reason of why it is dropped.
|
|
||||||
// The MessageWithID field in elem param can be queue.Pubrel or queue.Publish.
|
|
||||||
NotifyDropped(elem *Elem, err error)
|
|
||||||
NotifyInflightAdded(delta int)
|
|
||||||
NotifyMsgQueueAdded(delta int)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ElemExpiry return whether the elem is expired
|
|
||||||
func ElemExpiry(now time.Time, elem *Elem) bool {
|
|
||||||
if !elem.Expiry.IsZero() {
|
|
||||||
return now.After(elem.Expiry)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,209 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: persistence/queue/queue.go
|
|
||||||
|
|
||||||
// Package queue is a generated GoMock package.
|
|
||||||
package queue
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
packets "github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockStore is a mock of Store interface
|
|
||||||
type MockStore struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockStoreMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockStoreMockRecorder is the mock recorder for MockStore
|
|
||||||
type MockStoreMockRecorder struct {
|
|
||||||
mock *MockStore
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockStore creates a new mock instance
|
|
||||||
func NewMockStore(ctrl *gomock.Controller) *MockStore {
|
|
||||||
mock := &MockStore{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockStoreMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *MockStore) EXPECT() *MockStoreMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close mocks base method
|
|
||||||
func (m *MockStore) Close() error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Close")
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close indicates an expected call of Close
|
|
||||||
func (mr *MockStoreMockRecorder) Close() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStore)(nil).Close))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init mocks base method
|
|
||||||
func (m *MockStore) Init(opts *InitOptions) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Init", opts)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init indicates an expected call of Init
|
|
||||||
func (mr *MockStoreMockRecorder) Init(opts interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockStore)(nil).Init), opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean mocks base method
|
|
||||||
func (m *MockStore) Clean() error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Clean")
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean indicates an expected call of Clean
|
|
||||||
func (mr *MockStoreMockRecorder) Clean() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clean", reflect.TypeOf((*MockStore)(nil).Clean))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add mocks base method
|
|
||||||
func (m *MockStore) Add(elem *Elem) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Add", elem)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add indicates an expected call of Add
|
|
||||||
func (mr *MockStoreMockRecorder) Add(elem interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockStore)(nil).Add), elem)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace mocks base method
|
|
||||||
func (m *MockStore) Replace(elem *Elem) (bool, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Replace", elem)
|
|
||||||
ret0, _ := ret[0].(bool)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace indicates an expected call of Replace
|
|
||||||
func (mr *MockStoreMockRecorder) Replace(elem interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Replace", reflect.TypeOf((*MockStore)(nil).Replace), elem)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read mocks base method
|
|
||||||
func (m *MockStore) Read(pids []packets.PacketID) ([]*Elem, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Read", pids)
|
|
||||||
ret0, _ := ret[0].([]*Elem)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read indicates an expected call of Read
|
|
||||||
func (mr *MockStoreMockRecorder) Read(pids interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockStore)(nil).Read), pids)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadInflight mocks base method
|
|
||||||
func (m *MockStore) ReadInflight(maxSize uint) ([]*Elem, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "ReadInflight", maxSize)
|
|
||||||
ret0, _ := ret[0].([]*Elem)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadInflight indicates an expected call of ReadInflight
|
|
||||||
func (mr *MockStoreMockRecorder) ReadInflight(maxSize interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadInflight", reflect.TypeOf((*MockStore)(nil).ReadInflight), maxSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove mocks base method
|
|
||||||
func (m *MockStore) Remove(pid packets.PacketID) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Remove", pid)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove indicates an expected call of Remove
|
|
||||||
func (mr *MockStoreMockRecorder) Remove(pid interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockStore)(nil).Remove), pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockNotifier is a mock of Notifier interface
|
|
||||||
type MockNotifier struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockNotifierMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockNotifierMockRecorder is the mock recorder for MockNotifier
|
|
||||||
type MockNotifierMockRecorder struct {
|
|
||||||
mock *MockNotifier
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockNotifier creates a new mock instance
|
|
||||||
func NewMockNotifier(ctrl *gomock.Controller) *MockNotifier {
|
|
||||||
mock := &MockNotifier{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockNotifierMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *MockNotifier) EXPECT() *MockNotifierMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifyDropped mocks base method
|
|
||||||
func (m *MockNotifier) NotifyDropped(elem *Elem, err error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "NotifyDropped", elem, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifyDropped indicates an expected call of NotifyDropped
|
|
||||||
func (mr *MockNotifierMockRecorder) NotifyDropped(elem, err interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyDropped", reflect.TypeOf((*MockNotifier)(nil).NotifyDropped), elem, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifyInflightAdded mocks base method
|
|
||||||
func (m *MockNotifier) NotifyInflightAdded(delta int) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "NotifyInflightAdded", delta)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifyInflightAdded indicates an expected call of NotifyInflightAdded
|
|
||||||
func (mr *MockNotifierMockRecorder) NotifyInflightAdded(delta interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyInflightAdded", reflect.TypeOf((*MockNotifier)(nil).NotifyInflightAdded), delta)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifyMsgQueueAdded mocks base method
|
|
||||||
func (m *MockNotifier) NotifyMsgQueueAdded(delta int) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "NotifyMsgQueueAdded", delta)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifyMsgQueueAdded indicates an expected call of NotifyMsgQueueAdded
|
|
||||||
func (mr *MockNotifierMockRecorder) NotifyMsgQueueAdded(delta interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyMsgQueueAdded", reflect.TypeOf((*MockNotifier)(nil).NotifyMsgQueueAdded), delta)
|
|
||||||
}
|
|
@ -1,418 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
redigo "github.com/gomodule/redigo/redis"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/codes"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/queue"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
queuePrefix = "queue:"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ queue.Store = (*Queue)(nil)
|
|
||||||
|
|
||||||
func getKey(clientID string) string {
|
|
||||||
return queuePrefix + clientID
|
|
||||||
}
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
MaxQueuedMsg int
|
|
||||||
ClientID string
|
|
||||||
InflightExpiry time.Duration
|
|
||||||
Pool *redigo.Pool
|
|
||||||
DefaultNotifier queue.Notifier
|
|
||||||
}
|
|
||||||
|
|
||||||
type Queue struct {
|
|
||||||
cond *sync.Cond
|
|
||||||
clientID string
|
|
||||||
version packets.Version
|
|
||||||
readBytesLimit uint32
|
|
||||||
// max is the maximum queue length
|
|
||||||
max int
|
|
||||||
// len is the length of the list
|
|
||||||
len int
|
|
||||||
pool *redigo.Pool
|
|
||||||
closed bool
|
|
||||||
inflightDrained bool
|
|
||||||
// current is the current read index of Queue list.
|
|
||||||
current int
|
|
||||||
readCache map[packets.PacketID][]byte
|
|
||||||
err error
|
|
||||||
log *zap.Logger
|
|
||||||
inflightExpiry time.Duration
|
|
||||||
notifier queue.Notifier
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(opts Options) (*Queue, error) {
|
|
||||||
return &Queue{
|
|
||||||
cond: sync.NewCond(&sync.Mutex{}),
|
|
||||||
clientID: opts.ClientID,
|
|
||||||
max: opts.MaxQueuedMsg,
|
|
||||||
len: 0,
|
|
||||||
pool: opts.Pool,
|
|
||||||
closed: false,
|
|
||||||
inflightDrained: false,
|
|
||||||
current: 0,
|
|
||||||
inflightExpiry: opts.InflightExpiry,
|
|
||||||
notifier: opts.DefaultNotifier,
|
|
||||||
log: server.LoggerWithField(zap.String("queue", "redis")),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapError(err error) *codes.Error {
|
|
||||||
return &codes.Error{
|
|
||||||
Code: codes.UnspecifiedError,
|
|
||||||
ErrorDetails: codes.ErrorDetails{
|
|
||||||
ReasonString: []byte(err.Error()),
|
|
||||||
UserProperties: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Close() error {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer func() {
|
|
||||||
q.cond.L.Unlock()
|
|
||||||
q.cond.Signal()
|
|
||||||
}()
|
|
||||||
q.closed = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) setLen(conn redigo.Conn) error {
|
|
||||||
l, err := conn.Do("llen", getKey(q.clientID))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
q.len = int(l.(int64))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Init(opts *queue.InitOptions) error {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
conn := q.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
if opts.CleanStart {
|
|
||||||
_, err := conn.Do("del", getKey(q.clientID))
|
|
||||||
if err != nil {
|
|
||||||
return wrapError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := q.setLen(conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
q.version = opts.Version
|
|
||||||
q.readBytesLimit = opts.ReadBytesLimit
|
|
||||||
q.closed = false
|
|
||||||
q.inflightDrained = false
|
|
||||||
q.current = 0
|
|
||||||
q.readCache = make(map[packets.PacketID][]byte)
|
|
||||||
q.notifier = opts.Notifier
|
|
||||||
q.cond.Signal()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Clean() error {
|
|
||||||
conn := q.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
_, err := conn.Do("del", getKey(q.clientID))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Add(elem *queue.Elem) (err error) {
|
|
||||||
now := time.Now()
|
|
||||||
conn := q.pool.Get()
|
|
||||||
q.cond.L.Lock()
|
|
||||||
var dropErr error
|
|
||||||
var dropBytes []byte
|
|
||||||
var dropElem *queue.Elem
|
|
||||||
var drop bool
|
|
||||||
defer func() {
|
|
||||||
conn.Close()
|
|
||||||
q.cond.L.Unlock()
|
|
||||||
q.cond.Signal()
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if drop {
|
|
||||||
if dropErr == queue.ErrDropExpiredInflight {
|
|
||||||
q.notifier.NotifyInflightAdded(-1)
|
|
||||||
q.current--
|
|
||||||
}
|
|
||||||
if dropBytes == nil {
|
|
||||||
q.notifier.NotifyDropped(elem, dropErr)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
err = conn.Send("lrem", getKey(q.clientID), 1, dropBytes)
|
|
||||||
}
|
|
||||||
q.notifier.NotifyDropped(dropElem, dropErr)
|
|
||||||
} else {
|
|
||||||
q.notifier.NotifyMsgQueueAdded(1)
|
|
||||||
q.len++
|
|
||||||
}
|
|
||||||
_ = conn.Send("rpush", getKey(q.clientID), elem.Encode())
|
|
||||||
err = conn.Flush()
|
|
||||||
}()
|
|
||||||
if q.len >= q.max {
|
|
||||||
// set default drop error
|
|
||||||
dropErr = queue.ErrDropQueueFull
|
|
||||||
drop = true
|
|
||||||
var rs []interface{}
|
|
||||||
// drop expired inflight message
|
|
||||||
rs, err = redigo.Values(conn.Do("lrange", getKey(q.clientID), 0, q.len))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var frontBytes []byte
|
|
||||||
var frontElem *queue.Elem
|
|
||||||
for i := 0; i < len(rs); i++ {
|
|
||||||
b := rs[i].([]byte)
|
|
||||||
e := &queue.Elem{}
|
|
||||||
err = e.Decode(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// inflight message
|
|
||||||
if i < q.current && queue.ElemExpiry(now, e) {
|
|
||||||
dropBytes = b
|
|
||||||
dropElem = e
|
|
||||||
dropErr = queue.ErrDropExpiredInflight
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// non-inflight message
|
|
||||||
if i >= q.current {
|
|
||||||
if i == q.current {
|
|
||||||
frontBytes = b
|
|
||||||
frontElem = e
|
|
||||||
}
|
|
||||||
// drop qos0 message in the queue
|
|
||||||
pub := e.MessageWithID.(*queue.Publish)
|
|
||||||
// drop expired non-inflight message
|
|
||||||
if pub.ID() == 0 && queue.ElemExpiry(now, e) {
|
|
||||||
dropBytes = b
|
|
||||||
dropElem = e
|
|
||||||
dropErr = queue.ErrDropExpired
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if pub.ID() == 0 && pub.QoS == packets.Qos0 && dropElem == nil {
|
|
||||||
dropBytes = b
|
|
||||||
dropElem = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// drop the current elem if there is no more non-inflight messages.
|
|
||||||
if q.inflightDrained && q.current >= q.len {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rs, err = redigo.Values(conn.Do("lrange", getKey(q.clientID), q.current, q.len))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dropElem != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if elem.MessageWithID.(*queue.Publish).QoS == packets.Qos0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if frontElem != nil {
|
|
||||||
// drop the front message
|
|
||||||
dropBytes = frontBytes
|
|
||||||
dropElem = frontElem
|
|
||||||
}
|
|
||||||
// the the messages in the queue are all inflight messages, drop the current elem
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Replace(elem *queue.Elem) (replaced bool, err error) {
|
|
||||||
conn := q.pool.Get()
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer func() {
|
|
||||||
conn.Close()
|
|
||||||
q.cond.L.Unlock()
|
|
||||||
}()
|
|
||||||
id := elem.ID()
|
|
||||||
eb := elem.Encode()
|
|
||||||
stop := q.current - 1
|
|
||||||
if stop < 0 {
|
|
||||||
stop = 0
|
|
||||||
}
|
|
||||||
rs, err := redigo.Values(conn.Do("lrange", getKey(q.clientID), 0, stop))
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
for k, v := range rs {
|
|
||||||
b := v.([]byte)
|
|
||||||
e := &queue.Elem{}
|
|
||||||
err = e.Decode(b)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if e.ID() == elem.ID() {
|
|
||||||
_, err = conn.Do("lset", getKey(q.clientID), k, eb)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
q.readCache[id] = eb
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Read(pids []packets.PacketID) (elems []*queue.Elem, err error) {
|
|
||||||
now := time.Now()
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
conn := q.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
if !q.inflightDrained {
|
|
||||||
panic("must call ReadInflight to drain all inflight messages before Read")
|
|
||||||
}
|
|
||||||
for q.current >= q.len && !q.closed {
|
|
||||||
q.cond.Wait()
|
|
||||||
}
|
|
||||||
if q.closed {
|
|
||||||
return nil, queue.ErrClosed
|
|
||||||
}
|
|
||||||
rs, err := redigo.Values(conn.Do("lrange", getKey(q.clientID), q.current, q.current+len(pids)-1))
|
|
||||||
if err != nil {
|
|
||||||
return nil, wrapError(err)
|
|
||||||
}
|
|
||||||
var msgQueueDelta, inflightDelta int
|
|
||||||
var pflag int
|
|
||||||
for i := 0; i < len(rs); i++ {
|
|
||||||
b := rs[i].([]byte)
|
|
||||||
e := &queue.Elem{}
|
|
||||||
err := e.Decode(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// remove expired message
|
|
||||||
if queue.ElemExpiry(now, e) {
|
|
||||||
err = conn.Send("lrem", getKey(q.clientID), 1, b)
|
|
||||||
q.len--
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
q.notifier.NotifyDropped(e, queue.ErrDropExpired)
|
|
||||||
msgQueueDelta--
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove message which exceeds maximum packet size
|
|
||||||
pub := e.MessageWithID.(*queue.Publish)
|
|
||||||
if size := pub.TotalBytes(q.version); size > q.readBytesLimit {
|
|
||||||
err = conn.Send("lrem", getKey(q.clientID), 1, b)
|
|
||||||
q.len--
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
q.notifier.NotifyDropped(e, queue.ErrDropExceedsMaxPacketSize)
|
|
||||||
msgQueueDelta--
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.MessageWithID.(*queue.Publish).QoS == 0 {
|
|
||||||
err = conn.Send("lrem", getKey(q.clientID), 1, b)
|
|
||||||
q.len--
|
|
||||||
msgQueueDelta--
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
e.MessageWithID.SetID(pids[pflag])
|
|
||||||
if q.inflightExpiry != 0 {
|
|
||||||
e.Expiry = now.Add(q.inflightExpiry)
|
|
||||||
}
|
|
||||||
pflag++
|
|
||||||
nb := e.Encode()
|
|
||||||
|
|
||||||
err = conn.Send("lset", getKey(q.clientID), q.current, nb)
|
|
||||||
q.current++
|
|
||||||
inflightDelta++
|
|
||||||
q.readCache[e.MessageWithID.ID()] = nb
|
|
||||||
}
|
|
||||||
elems = append(elems, e)
|
|
||||||
}
|
|
||||||
err = conn.Flush()
|
|
||||||
q.notifier.NotifyMsgQueueAdded(msgQueueDelta)
|
|
||||||
q.notifier.NotifyInflightAdded(inflightDelta)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) ReadInflight(maxSize uint) (elems []*queue.Elem, err error) {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
conn := q.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
rs, err := redigo.Values(conn.Do("lrange", getKey(q.clientID), q.current, q.current+int(maxSize)-1))
|
|
||||||
if len(rs) == 0 {
|
|
||||||
q.inflightDrained = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, wrapError(err)
|
|
||||||
}
|
|
||||||
beginIndex := q.current
|
|
||||||
for index, v := range rs {
|
|
||||||
b := v.([]byte)
|
|
||||||
e := &queue.Elem{}
|
|
||||||
err := e.Decode(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
id := e.MessageWithID.ID()
|
|
||||||
if id != 0 {
|
|
||||||
if q.inflightExpiry != 0 {
|
|
||||||
e.Expiry = time.Now().Add(q.inflightExpiry)
|
|
||||||
b = e.Encode()
|
|
||||||
_, err = conn.Do("lset", getKey(q.clientID), beginIndex+index, b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elems = append(elems, e)
|
|
||||||
q.readCache[id] = b
|
|
||||||
q.current++
|
|
||||||
} else {
|
|
||||||
q.inflightDrained = true
|
|
||||||
return elems, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) Remove(pid packets.PacketID) error {
|
|
||||||
q.cond.L.Lock()
|
|
||||||
defer q.cond.L.Unlock()
|
|
||||||
conn := q.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
if b, ok := q.readCache[pid]; ok {
|
|
||||||
_, err := conn.Do("lrem", getKey(q.clientID), 1, b)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
q.notifier.NotifyMsgQueueAdded(-1)
|
|
||||||
q.notifier.NotifyInflightAdded(-1)
|
|
||||||
delete(q.readCache, pid)
|
|
||||||
q.len--
|
|
||||||
q.current--
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,672 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/queue"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
TestServerConfig = config.Config{
|
|
||||||
MQTT: config.MQTT{
|
|
||||||
MaxQueuedMsg: 5,
|
|
||||||
InflightExpiry: 2 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cid = "cid"
|
|
||||||
TestClientID = cid
|
|
||||||
TestNotifier = &testNotifier{}
|
|
||||||
)
|
|
||||||
|
|
||||||
type testNotifier struct {
|
|
||||||
dropElem []*queue.Elem
|
|
||||||
dropErr error
|
|
||||||
inflightLen int
|
|
||||||
msgQueueLen int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testNotifier) NotifyDropped(elem *queue.Elem, err error) {
|
|
||||||
t.dropElem = append(t.dropElem, elem)
|
|
||||||
t.dropErr = err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testNotifier) NotifyInflightAdded(delta int) {
|
|
||||||
t.inflightLen += delta
|
|
||||||
if t.inflightLen < 0 {
|
|
||||||
t.inflightLen = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testNotifier) NotifyMsgQueueAdded(delta int) {
|
|
||||||
t.msgQueueLen += delta
|
|
||||||
if t.msgQueueLen < 0 {
|
|
||||||
t.msgQueueLen = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDrop() {
|
|
||||||
TestNotifier.dropElem = nil
|
|
||||||
TestNotifier.dropErr = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initNotifierLen() {
|
|
||||||
TestNotifier.inflightLen = 0
|
|
||||||
TestNotifier.msgQueueLen = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertMsgEqual(a *assert.Assertions, expected, actual *queue.Elem) {
|
|
||||||
expMsg := expected.MessageWithID.(*queue.Publish).Message
|
|
||||||
actMsg := actual.MessageWithID.(*queue.Publish).Message
|
|
||||||
a.Equal(expMsg.Topic, actMsg.Topic)
|
|
||||||
a.Equal(expMsg.QoS, actMsg.QoS)
|
|
||||||
a.Equal(expMsg.Payload, actMsg.Payload)
|
|
||||||
a.Equal(expMsg.PacketID, actMsg.PacketID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertQueueLen(a *assert.Assertions, inflightLen, msgQueueLen int) {
|
|
||||||
a.Equal(inflightLen, TestNotifier.inflightLen)
|
|
||||||
a.Equal(msgQueueLen, TestNotifier.msgQueueLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2 inflight message + 3 new message
|
|
||||||
var initElems = []*queue.Elem{
|
|
||||||
{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: packets.Qos1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/topic1_qos1",
|
|
||||||
Payload: []byte("qos1"),
|
|
||||||
PacketID: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: packets.Qos2,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/topic1_qos2",
|
|
||||||
Payload: []byte("qos2"),
|
|
||||||
PacketID: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: packets.Qos1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/topic1_qos1",
|
|
||||||
Payload: []byte("qos1"),
|
|
||||||
PacketID: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: packets.Qos0,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/topic1_qos0",
|
|
||||||
Payload: []byte("qos0"),
|
|
||||||
PacketID: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: packets.Qos2,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/topic1_qos2",
|
|
||||||
Payload: []byte("qos2"),
|
|
||||||
PacketID: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func initStore(store queue.Store) error {
|
|
||||||
return store.Init(&queue.InitOptions{
|
|
||||||
CleanStart: true,
|
|
||||||
Version: packets.Version5,
|
|
||||||
ReadBytesLimit: 100,
|
|
||||||
Notifier: TestNotifier,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func add(store queue.Store) error {
|
|
||||||
for _, v := range initElems {
|
|
||||||
elem := *v
|
|
||||||
elem.MessageWithID = &queue.Publish{
|
|
||||||
Message: elem.MessageWithID.(*queue.Publish).Message.Copy(),
|
|
||||||
}
|
|
||||||
err := store.Add(&elem)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TestNotifier.inflightLen = 2
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertDrop(a *assert.Assertions, elem *queue.Elem, err error) {
|
|
||||||
a.Len(TestNotifier.dropElem, 1)
|
|
||||||
switch elem.MessageWithID.(type) {
|
|
||||||
case *queue.Publish:
|
|
||||||
actual := TestNotifier.dropElem[0].MessageWithID.(*queue.Publish)
|
|
||||||
pub := elem.MessageWithID.(*queue.Publish)
|
|
||||||
a.Equal(pub.Message.Topic, actual.Topic)
|
|
||||||
a.Equal(pub.Message.QoS, actual.QoS)
|
|
||||||
a.Equal(pub.Payload, actual.Payload)
|
|
||||||
a.Equal(pub.PacketID, actual.PacketID)
|
|
||||||
a.Equal(err, TestNotifier.dropErr)
|
|
||||||
case *queue.Pubrel:
|
|
||||||
actual := TestNotifier.dropElem[0].MessageWithID.(*queue.Pubrel)
|
|
||||||
pubrel := elem.MessageWithID.(*queue.Pubrel)
|
|
||||||
a.Equal(pubrel.PacketID, actual.PacketID)
|
|
||||||
a.Equal(err, TestNotifier.dropErr)
|
|
||||||
default:
|
|
||||||
a.FailNow("unexpected elem type")
|
|
||||||
|
|
||||||
}
|
|
||||||
initDrop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func reconnect(a *assert.Assertions, cleanStart bool, store queue.Store) {
|
|
||||||
a.NoError(store.Close())
|
|
||||||
a.NoError(store.Init(&queue.InitOptions{
|
|
||||||
CleanStart: cleanStart,
|
|
||||||
Version: packets.Version5,
|
|
||||||
ReadBytesLimit: 100,
|
|
||||||
Notifier: TestNotifier,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
type New func(config config.Config, hooks server.Hooks) (server.Persistence, error)
|
|
||||||
|
|
||||||
func TestQueue(t *testing.T, store queue.Store) {
|
|
||||||
initDrop()
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
a.NoError(initStore(store))
|
|
||||||
a.NoError(add(store))
|
|
||||||
assertQueueLen(a, 2, 5)
|
|
||||||
testRead(a, store)
|
|
||||||
testDrop(a, store)
|
|
||||||
testReplace(a, store)
|
|
||||||
testCleanStart(a, store)
|
|
||||||
testReadExceedsDrop(a, store)
|
|
||||||
testClose(a, store)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testDrop(a *assert.Assertions, store queue.Store) {
|
|
||||||
// wait inflight messages to expire
|
|
||||||
time.Sleep(TestServerConfig.MQTT.InflightExpiry)
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
err := store.Add(&queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 2,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "123",
|
|
||||||
Payload: []byte("123"),
|
|
||||||
PacketID: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
}
|
|
||||||
// drop expired inflight message (pid=1)
|
|
||||||
dropElem := initElems[0]
|
|
||||||
// queue: 1,2,0(qos2),0(qos2),0(qos2) (1 and 2 are expired inflight messages)
|
|
||||||
err := store.Add(&queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "123",
|
|
||||||
Payload: []byte("123"),
|
|
||||||
PacketID: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
a.NoError(err)
|
|
||||||
assertDrop(a, dropElem, queue.ErrDropExpiredInflight)
|
|
||||||
assertQueueLen(a, 1, 5)
|
|
||||||
|
|
||||||
e, err := store.Read([]packets.PacketID{5, 6, 7})
|
|
||||||
a.NoError(err)
|
|
||||||
a.Len(e, 3)
|
|
||||||
a.EqualValues(5, e[0].MessageWithID.ID())
|
|
||||||
a.EqualValues(6, e[1].MessageWithID.ID())
|
|
||||||
a.EqualValues(7, e[2].MessageWithID.ID())
|
|
||||||
// queue: 2,5(qos2),6(qos2),7(qos2), 0(qos1) (2 is expired inflight message)
|
|
||||||
assertQueueLen(a, 4, 5)
|
|
||||||
// drop expired inflight message (pid=2)
|
|
||||||
dropElem = initElems[1]
|
|
||||||
err = store.Add(&queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "1234",
|
|
||||||
Payload: []byte("1234"),
|
|
||||||
PacketID: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
a.NoError(err)
|
|
||||||
// queue: 5(qos2),6(qos2),7(qos2), 0(qos1), 0(qos1)
|
|
||||||
assertDrop(a, dropElem, queue.ErrDropExpiredInflight)
|
|
||||||
|
|
||||||
assertQueueLen(a, 3, 5)
|
|
||||||
e, err = store.Read([]packets.PacketID{8, 9})
|
|
||||||
|
|
||||||
a.NoError(err)
|
|
||||||
|
|
||||||
// queue: 5(qos2),6(qos2),7(qos2),8(qos1),9(qos1)
|
|
||||||
a.Len(e, 2)
|
|
||||||
a.EqualValues(8, e[0].MessageWithID.ID())
|
|
||||||
a.EqualValues(9, e[1].MessageWithID.ID())
|
|
||||||
assertQueueLen(a, 5, 5)
|
|
||||||
|
|
||||||
// drop the elem that is going to enqueue if there is no more non-inflight messages.
|
|
||||||
dropElem = &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "123",
|
|
||||||
Payload: []byte("123"),
|
|
||||||
PacketID: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
err = store.Add(dropElem)
|
|
||||||
a.NoError(err)
|
|
||||||
assertDrop(a, dropElem, queue.ErrDropQueueFull)
|
|
||||||
assertQueueLen(a, 5, 5)
|
|
||||||
|
|
||||||
// queue: 5(qos2),6(qos2),7(qos2),8(qos1),9(qos1)
|
|
||||||
a.NoError(store.Remove(5))
|
|
||||||
a.NoError(store.Remove(6))
|
|
||||||
// queue: 7(qos2),8(qos2),9(qos2)
|
|
||||||
assertQueueLen(a, 3, 3)
|
|
||||||
|
|
||||||
dropQoS0 := &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 0,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/t_qos0",
|
|
||||||
Payload: []byte("test"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
a.NoError(store.Add(dropQoS0))
|
|
||||||
// queue: 7(qos2),8(qos2),9(qos2),0 (qos0/t_qos0)
|
|
||||||
assertQueueLen(a, 3, 4)
|
|
||||||
|
|
||||||
// add expired elem
|
|
||||||
dropExpired := &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Now().Add(-10 * time.Second),
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 0,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/drop",
|
|
||||||
Payload: []byte("test"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
a.NoError(store.Add(dropExpired))
|
|
||||||
// queue: 7(qos2),8(qos2),9(qos2), 0(qos0/t_qos0), 0(qos0/drop)
|
|
||||||
assertQueueLen(a, 3, 5)
|
|
||||||
dropFront := &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/drop_front",
|
|
||||||
Payload: []byte("drop_front"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// drop the expired non-inflight message
|
|
||||||
a.NoError(store.Add(dropFront))
|
|
||||||
// queue: 7(qos2),8(qos2),9(qos2), 0(qos0/t_qos0), 0(qos1/drop_front)
|
|
||||||
assertDrop(a, dropExpired, queue.ErrDropExpired)
|
|
||||||
assertQueueLen(a, 3, 5)
|
|
||||||
|
|
||||||
// drop qos0 message
|
|
||||||
a.Nil(store.Add(&queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: packets.Qos1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/t_qos1",
|
|
||||||
Payload: []byte("/t_qos1"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
// queue: 7(qos2),8(qos2),9(qos2), 0(qos1/drop_front), 0(qos1/t_qos1)
|
|
||||||
assertDrop(a, dropQoS0, queue.ErrDropQueueFull)
|
|
||||||
assertQueueLen(a, 3, 5)
|
|
||||||
|
|
||||||
expiredPub := &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Now().Add(TestServerConfig.MQTT.InflightExpiry),
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/t",
|
|
||||||
Payload: []byte("/t"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
a.NoError(store.Add(expiredPub))
|
|
||||||
// drop the front message
|
|
||||||
assertDrop(a, dropFront, queue.ErrDropQueueFull)
|
|
||||||
// queue: 7(qos2),8(qos2),9(qos2), 0(qos1/t_qos1), 0(qos1/t)
|
|
||||||
assertQueueLen(a, 3, 5)
|
|
||||||
// replace with an expired pubrel
|
|
||||||
expiredPubrel := &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Now().Add(-1 * time.Second),
|
|
||||||
MessageWithID: &queue.Pubrel{
|
|
||||||
PacketID: 7,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
r, err := store.Replace(expiredPubrel)
|
|
||||||
a.True(r)
|
|
||||||
a.NoError(err)
|
|
||||||
assertQueueLen(a, 3, 5)
|
|
||||||
// queue: 7(qos2-pubrel),8(qos2),9(qos2), 0(qos1/t_qos1), 0(qos1/t)
|
|
||||||
a.NoError(store.Add(&queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/t1",
|
|
||||||
Payload: []byte("/t1"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
// queue: 8(qos2),9(qos2), 0(qos1/t_qos1), 0(qos1/t), 0(qos1/t1)
|
|
||||||
assertDrop(a, expiredPubrel, queue.ErrDropExpiredInflight)
|
|
||||||
assertQueueLen(a, 2, 5)
|
|
||||||
drop := &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 0,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/t2",
|
|
||||||
Payload: []byte("/t2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
a.NoError(store.Add(drop))
|
|
||||||
assertDrop(a, drop, queue.ErrDropQueueFull)
|
|
||||||
assertQueueLen(a, 2, 5)
|
|
||||||
|
|
||||||
a.NoError(store.Remove(8))
|
|
||||||
a.NoError(store.Remove(9))
|
|
||||||
// queue: 0(qos1/t_qos1), 0(qos1/t), 0(qos1/t1)
|
|
||||||
assertQueueLen(a, 0, 3)
|
|
||||||
// wait qos1/t to expire.
|
|
||||||
time.Sleep(TestServerConfig.MQTT.InflightExpiry)
|
|
||||||
e, err = store.Read([]packets.PacketID{1, 2, 3})
|
|
||||||
a.NoError(err)
|
|
||||||
a.Len(e, 2)
|
|
||||||
assertQueueLen(a, 2, 2)
|
|
||||||
a.NoError(store.Remove(1))
|
|
||||||
a.NoError(store.Remove(2))
|
|
||||||
assertQueueLen(a, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRead(a *assert.Assertions, store queue.Store) {
|
|
||||||
// 2 inflight
|
|
||||||
e, err := store.ReadInflight(1)
|
|
||||||
a.Nil(err)
|
|
||||||
a.Len(e, 1)
|
|
||||||
assertMsgEqual(a, initElems[0], e[0])
|
|
||||||
|
|
||||||
e, err = store.ReadInflight(2)
|
|
||||||
a.Len(e, 1)
|
|
||||||
assertMsgEqual(a, initElems[1], e[0])
|
|
||||||
pids := []packets.PacketID{3, 4, 5}
|
|
||||||
e, err = store.Read(pids)
|
|
||||||
a.Len(e, 3)
|
|
||||||
|
|
||||||
// must consume packet id in order and do not skip packet id if there are qos0 messages.
|
|
||||||
a.EqualValues(3, e[0].MessageWithID.ID())
|
|
||||||
a.EqualValues(0, e[1].MessageWithID.ID())
|
|
||||||
a.EqualValues(4, e[2].MessageWithID.ID())
|
|
||||||
|
|
||||||
assertQueueLen(a, 4, 4)
|
|
||||||
|
|
||||||
err = store.Remove(3)
|
|
||||||
a.NoError(err)
|
|
||||||
err = store.Remove(4)
|
|
||||||
a.NoError(err)
|
|
||||||
assertQueueLen(a, 2, 2)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func testReplace(a *assert.Assertions, store queue.Store) {
|
|
||||||
|
|
||||||
var elems []*queue.Elem
|
|
||||||
elems = append(elems, &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: 2,
|
|
||||||
Topic: "/t_replace",
|
|
||||||
Payload: []byte("t_replace"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: 2,
|
|
||||||
Topic: "/t_replace",
|
|
||||||
Payload: []byte("t_replace"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: 2,
|
|
||||||
Topic: "/t_unread",
|
|
||||||
Payload: []byte("t_unread"),
|
|
||||||
PacketID: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
for i := 0; i < 2; i++ {
|
|
||||||
elems = append(elems, &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
QoS: 2,
|
|
||||||
Topic: "/t_replace",
|
|
||||||
Payload: []byte("t_replace"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
a.NoError(store.Add(elems[i]))
|
|
||||||
}
|
|
||||||
assertQueueLen(a, 0, 2)
|
|
||||||
|
|
||||||
e, err := store.Read([]packets.PacketID{1, 2})
|
|
||||||
// queue: 1(qos2),2(qos2)
|
|
||||||
a.NoError(err)
|
|
||||||
a.Len(e, 2)
|
|
||||||
assertQueueLen(a, 2, 2)
|
|
||||||
r, err := store.Replace(&queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Pubrel{
|
|
||||||
PacketID: 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
a.True(r)
|
|
||||||
a.NoError(err)
|
|
||||||
|
|
||||||
r, err = store.Replace(&queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Pubrel{
|
|
||||||
PacketID: 3,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
a.False(r)
|
|
||||||
a.NoError(err)
|
|
||||||
a.NoError(store.Add(elems[2]))
|
|
||||||
TestNotifier.inflightLen++
|
|
||||||
// queue: 1(qos2-pubrel),2(qos2), 3(qos2)
|
|
||||||
|
|
||||||
r, err = store.Replace(&queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
Expiry: time.Time{},
|
|
||||||
MessageWithID: &queue.Pubrel{
|
|
||||||
PacketID: packets.PacketID(3),
|
|
||||||
}})
|
|
||||||
a.False(r, "must not replace unread packet")
|
|
||||||
a.NoError(err)
|
|
||||||
assertQueueLen(a, 3, 3)
|
|
||||||
|
|
||||||
reconnect(a, false, store)
|
|
||||||
|
|
||||||
inflight, err := store.ReadInflight(5)
|
|
||||||
a.NoError(err)
|
|
||||||
a.Len(inflight, 3)
|
|
||||||
a.Equal(&queue.Pubrel{
|
|
||||||
PacketID: 1,
|
|
||||||
}, inflight[0].MessageWithID)
|
|
||||||
|
|
||||||
elems[1].MessageWithID.SetID(2)
|
|
||||||
elems[2].MessageWithID.SetID(3)
|
|
||||||
assertMsgEqual(a, elems[1], inflight[1])
|
|
||||||
assertMsgEqual(a, elems[2], inflight[2])
|
|
||||||
assertQueueLen(a, 3, 3)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func testReadExceedsDrop(a *assert.Assertions, store queue.Store) {
|
|
||||||
// add exceeded message
|
|
||||||
exceeded := &queue.Elem{
|
|
||||||
At: time.Now(),
|
|
||||||
MessageWithID: &queue.Publish{
|
|
||||||
Message: &gmqtt.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: 1,
|
|
||||||
Retained: false,
|
|
||||||
Topic: "/drop_exceed",
|
|
||||||
Payload: make([]byte, 100),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
a.NoError(store.Add(exceeded))
|
|
||||||
assertQueueLen(a, 0, 1)
|
|
||||||
e, err := store.Read([]packets.PacketID{1})
|
|
||||||
a.NoError(err)
|
|
||||||
a.Len(e, 0)
|
|
||||||
assertDrop(a, exceeded, queue.ErrDropExceedsMaxPacketSize)
|
|
||||||
assertQueueLen(a, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCleanStart(a *assert.Assertions, store queue.Store) {
|
|
||||||
reconnect(a, true, store)
|
|
||||||
rs, err := store.ReadInflight(10)
|
|
||||||
a.NoError(err)
|
|
||||||
a.Len(rs, 0)
|
|
||||||
initDrop()
|
|
||||||
initNotifierLen()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testClose(a *assert.Assertions, store queue.Store) {
|
|
||||||
t := time.After(2 * time.Second)
|
|
||||||
result := make(chan struct {
|
|
||||||
len int
|
|
||||||
err error
|
|
||||||
})
|
|
||||||
go func() {
|
|
||||||
// should block
|
|
||||||
rs, err := store.Read([]packets.PacketID{1, 2, 3})
|
|
||||||
result <- struct {
|
|
||||||
len int
|
|
||||||
err error
|
|
||||||
}{len: len(rs), err: err}
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <-result:
|
|
||||||
a.Fail("Read must be blocked before Close")
|
|
||||||
case <-t:
|
|
||||||
}
|
|
||||||
a.NoError(store.Close())
|
|
||||||
timeout := time.After(5 * time.Second)
|
|
||||||
select {
|
|
||||||
case <-timeout:
|
|
||||||
a.Fail("Read must be unblocked after Close")
|
|
||||||
case r := <-result:
|
|
||||||
a.Zero(r.len)
|
|
||||||
a.Equal(queue.ErrClosed, r.err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
redigo "github.com/gomodule/redigo/redis"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/queue"
|
|
||||||
redis_queue "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/queue/redis"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/session"
|
|
||||||
redis_sess "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/session/redis"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription"
|
|
||||||
redis_sub "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription/redis"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/unack"
|
|
||||||
redis_unack "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/unack/redis"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
server.RegisterPersistenceFactory("redis", NewRedis)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRedis(config config.Config) (server.Persistence, error) {
|
|
||||||
return &redis{
|
|
||||||
config: config,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type redis struct {
|
|
||||||
pool *redigo.Pool
|
|
||||||
config config.Config
|
|
||||||
onMsgDropped server.OnMsgDropped
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *redis) NewUnackStore(config config.Config, clientID string) (unack.Store, error) {
|
|
||||||
return redis_unack.New(redis_unack.Options{
|
|
||||||
ClientID: clientID,
|
|
||||||
Pool: r.pool,
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *redis) NewSessionStore(config config.Config) (session.Store, error) {
|
|
||||||
return redis_sess.New(r.pool), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPool(config config.Config) *redigo.Pool {
|
|
||||||
return &redigo.Pool{
|
|
||||||
// Dial or DialContext must be set. When both are set, DialContext takes precedence over Dial.
|
|
||||||
Dial: func() (redigo.Conn, error) {
|
|
||||||
c, err := redigo.Dial("tcp", config.Persistence.Redis.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if pswd := config.Persistence.Redis.Password; pswd != "" {
|
|
||||||
if _, err := c.Do("AUTH", pswd); err != nil {
|
|
||||||
c.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := c.Do("SELECT", config.Persistence.Redis.Database); err != nil {
|
|
||||||
c.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (r *redis) Open() error {
|
|
||||||
r.pool = newPool(r.config)
|
|
||||||
r.pool.MaxIdle = int(*r.config.Persistence.Redis.MaxIdle)
|
|
||||||
r.pool.MaxActive = int(*r.config.Persistence.Redis.MaxActive)
|
|
||||||
r.pool.IdleTimeout = r.config.Persistence.Redis.IdleTimeout
|
|
||||||
conn := r.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
// Test the connection
|
|
||||||
_, err := conn.Do("PING")
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *redis) NewQueueStore(config config.Config, defaultNotifier queue.Notifier, clientID string) (queue.Store, error) {
|
|
||||||
return redis_queue.New(redis_queue.Options{
|
|
||||||
MaxQueuedMsg: config.MQTT.MaxQueuedMsg,
|
|
||||||
InflightExpiry: config.MQTT.InflightExpiry,
|
|
||||||
ClientID: clientID,
|
|
||||||
Pool: r.pool,
|
|
||||||
DefaultNotifier: defaultNotifier,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *redis) NewSubscriptionStore(config config.Config) (subscription.Store, error) {
|
|
||||||
return redis_sub.New(r.pool), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *redis) Close() error {
|
|
||||||
return r.pool.Close()
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
package mem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/session"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ session.Store = (*Store)(nil)
|
|
||||||
|
|
||||||
func New() *Store {
|
|
||||||
return &Store{
|
|
||||||
mu: sync.Mutex{},
|
|
||||||
sess: make(map[string]*gmqtt.Session),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Store struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
sess map[string]*gmqtt.Session
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Set(session *gmqtt.Session) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
s.sess[session.ClientID] = session
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Remove(clientID string) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
delete(s.sess, clientID)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Get(clientID string) (*gmqtt.Session, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
return s.sess[clientID], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) GetAll() ([]*gmqtt.Session, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) SetSessionExpiry(clientID string, expiry uint32) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
if s, ok := s.sess[clientID]; ok {
|
|
||||||
s.ExpiryInterval = expiry
|
|
||||||
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Iterate(fn session.IterateFn) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
for _, v := range s.sess {
|
|
||||||
cont := fn(v)
|
|
||||||
if !cont {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gomodule/redigo/redis"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/encoding"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/session"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
sessPrefix = "session:"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ session.Store = (*Store)(nil)
|
|
||||||
|
|
||||||
type Store struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
pool *redis.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(pool *redis.Pool) *Store {
|
|
||||||
return &Store{
|
|
||||||
mu: sync.Mutex{},
|
|
||||||
pool: pool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getKey(clientID string) string {
|
|
||||||
return sessPrefix + clientID
|
|
||||||
}
|
|
||||||
func (s *Store) Set(session *gmqtt.Session) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
encoding.EncodeMessage(session.Will, b)
|
|
||||||
_, err := c.Do("hset", getKey(session.ClientID),
|
|
||||||
"client_id", session.ClientID,
|
|
||||||
"will", b.Bytes(),
|
|
||||||
"will_delay_interval", session.WillDelayInterval,
|
|
||||||
"connected_at", session.ConnectedAt.Unix(),
|
|
||||||
"expiry_interval", session.ExpiryInterval,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Remove(clientID string) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
_, err := c.Do("del", getKey(clientID))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Get(clientID string) (*gmqtt.Session, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
return getSessionLocked(getKey(clientID), c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSessionLocked(key string, c redis.Conn) (*gmqtt.Session, error) {
|
|
||||||
replay, err := redis.Values(c.Do("hmget", key, "client_id", "will", "will_delay_interval", "connected_at", "expiry_interval"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sess := &gmqtt.Session{}
|
|
||||||
var connectedAt uint32
|
|
||||||
var will []byte
|
|
||||||
_, err = redis.Scan(replay, &sess.ClientID, &will, &sess.WillDelayInterval, &connectedAt, &sess.ExpiryInterval)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sess.ConnectedAt = time.Unix(int64(connectedAt), 0)
|
|
||||||
sess.Will, err = encoding.DecodeMessageFromBytes(will)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return sess, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) SetSessionExpiry(clientID string, expiry uint32) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
_, err := c.Do("hset", getKey(clientID),
|
|
||||||
"expiry_interval", expiry,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Iterate(fn session.IterateFn) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
iter := 0
|
|
||||||
for {
|
|
||||||
arr, err := redis.Values(c.Do("SCAN", iter, "MATCH", sessPrefix+"*"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(arr) >= 1 {
|
|
||||||
for _, v := range arr[1:] {
|
|
||||||
for _, vv := range v.([]interface{}) {
|
|
||||||
sess, err := getSessionLocked(string(vv.([]uint8)), c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cont := fn(sess)
|
|
||||||
if !cont {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iter, _ = redis.Int(arr[0], nil)
|
|
||||||
if iter == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package session
|
|
||||||
|
|
||||||
import gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
|
|
||||||
// IterateFn is the callback function used by Iterate()
|
|
||||||
// Return false means to stop the iteration.
|
|
||||||
type IterateFn func(session *gmqtt.Session) bool
|
|
||||||
|
|
||||||
type Store interface {
|
|
||||||
Set(session *gmqtt.Session) error
|
|
||||||
Remove(clientID string) error
|
|
||||||
Get(clientID string) (*gmqtt.Session, error)
|
|
||||||
Iterate(fn IterateFn) error
|
|
||||||
SetSessionExpiry(clientID string, expiry uint32) error
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: persistence/session/session.go
|
|
||||||
|
|
||||||
// Package session is a generated GoMock package.
|
|
||||||
package session
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockStore is a mock of Store interface
|
|
||||||
type MockStore struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockStoreMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockStoreMockRecorder is the mock recorder for MockStore
|
|
||||||
type MockStoreMockRecorder struct {
|
|
||||||
mock *MockStore
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockStore creates a new mock instance
|
|
||||||
func NewMockStore(ctrl *gomock.Controller) *MockStore {
|
|
||||||
mock := &MockStore{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockStoreMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *MockStore) EXPECT() *MockStoreMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set mocks base method
|
|
||||||
func (m *MockStore) Set(session *gmqtt.Session) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Set", session)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set indicates an expected call of Set
|
|
||||||
func (mr *MockStoreMockRecorder) Set(session interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockStore)(nil).Set), session)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove mocks base method
|
|
||||||
func (m *MockStore) Remove(clientID string) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Remove", clientID)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove indicates an expected call of Remove
|
|
||||||
func (mr *MockStoreMockRecorder) Remove(clientID interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockStore)(nil).Remove), clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get mocks base method
|
|
||||||
func (m *MockStore) Get(clientID string) (*gmqtt.Session, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Get", clientID)
|
|
||||||
ret0, _ := ret[0].(*gmqtt.Session)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get indicates an expected call of Get
|
|
||||||
func (mr *MockStoreMockRecorder) Get(clientID interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockStore)(nil).Get), clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate mocks base method
|
|
||||||
func (m *MockStore) Iterate(fn IterateFn) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Iterate", fn)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate indicates an expected call of Iterate
|
|
||||||
func (mr *MockStoreMockRecorder) Iterate(fn interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iterate", reflect.TypeOf((*MockStore)(nil).Iterate), fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSessionExpiry mocks base method
|
|
||||||
func (m *MockStore) SetSessionExpiry(clientID string, expiry uint32) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "SetSessionExpiry", clientID, expiry)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSessionExpiry indicates an expected call of SetSessionExpiry
|
|
||||||
func (mr *MockStoreMockRecorder) SetSessionExpiry(clientID, expiry interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSessionExpiry", reflect.TypeOf((*MockStore)(nil).SetSessionExpiry), clientID, expiry)
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/session"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSuite(t *testing.T, store session.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
var tt = []*gmqtt.Session{
|
|
||||||
{
|
|
||||||
ClientID: "client",
|
|
||||||
Will: &gmqtt.Message{
|
|
||||||
Topic: "topicA",
|
|
||||||
Payload: []byte("abc"),
|
|
||||||
},
|
|
||||||
WillDelayInterval: 1,
|
|
||||||
ConnectedAt: time.Unix(1, 0),
|
|
||||||
ExpiryInterval: 2,
|
|
||||||
}, {
|
|
||||||
ClientID: "client2",
|
|
||||||
Will: nil,
|
|
||||||
WillDelayInterval: 0,
|
|
||||||
ConnectedAt: time.Unix(2, 0),
|
|
||||||
ExpiryInterval: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
a.Nil(store.Set(v))
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
sess, err := store.Get(v.ClientID)
|
|
||||||
a.Nil(err)
|
|
||||||
a.EqualValues(v, sess)
|
|
||||||
}
|
|
||||||
var sess []*gmqtt.Session
|
|
||||||
err := store.Iterate(func(session *gmqtt.Session) bool {
|
|
||||||
sess = append(sess, session)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
a.ElementsMatch(sess, tt)
|
|
||||||
}
|
|
@ -1,201 +0,0 @@
|
|||||||
package mem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription"
|
|
||||||
)
|
|
||||||
|
|
||||||
// topicTrie
|
|
||||||
type topicTrie = topicNode
|
|
||||||
|
|
||||||
// children
|
|
||||||
type children = map[string]*topicNode
|
|
||||||
|
|
||||||
type clientOpts map[string]*gmqtt.Subscription
|
|
||||||
|
|
||||||
// topicNode
|
|
||||||
type topicNode struct {
|
|
||||||
children children
|
|
||||||
// clients store non-share subscription
|
|
||||||
clients clientOpts
|
|
||||||
parent *topicNode // pointer of parent node
|
|
||||||
topicName string
|
|
||||||
// shared store shared subscription, key by ShareName
|
|
||||||
shared map[string]clientOpts
|
|
||||||
}
|
|
||||||
|
|
||||||
// newTopicTrie create a new trie tree
|
|
||||||
func newTopicTrie() *topicTrie {
|
|
||||||
return newNode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNode create a new trie node
|
|
||||||
func newNode() *topicNode {
|
|
||||||
return &topicNode{
|
|
||||||
children: children{},
|
|
||||||
clients: make(clientOpts),
|
|
||||||
shared: make(map[string]clientOpts),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newChild create a child node of t
|
|
||||||
func (t *topicNode) newChild() *topicNode {
|
|
||||||
n := newNode()
|
|
||||||
n.parent = t
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// subscribe add a subscription and return the added node
|
|
||||||
func (t *topicTrie) subscribe(clientID string, s *gmqtt.Subscription) *topicNode {
|
|
||||||
topicSlice := strings.Split(s.TopicFilter, "/")
|
|
||||||
var pNode = t
|
|
||||||
for _, lv := range topicSlice {
|
|
||||||
if _, ok := pNode.children[lv]; !ok {
|
|
||||||
pNode.children[lv] = pNode.newChild()
|
|
||||||
}
|
|
||||||
pNode = pNode.children[lv]
|
|
||||||
}
|
|
||||||
// shared subscription
|
|
||||||
if s.ShareName != "" {
|
|
||||||
if pNode.shared[s.ShareName] == nil {
|
|
||||||
pNode.shared[s.ShareName] = make(clientOpts)
|
|
||||||
}
|
|
||||||
pNode.shared[s.ShareName][clientID] = s
|
|
||||||
} else {
|
|
||||||
// non-shared
|
|
||||||
pNode.clients[clientID] = s
|
|
||||||
}
|
|
||||||
pNode.topicName = s.TopicFilter
|
|
||||||
return pNode
|
|
||||||
}
|
|
||||||
|
|
||||||
// find walk through the tire and return the node that represent the topicFilter.
|
|
||||||
// Return nil if not found
|
|
||||||
func (t *topicTrie) find(topicFilter string) *topicNode {
|
|
||||||
topicSlice := strings.Split(topicFilter, "/")
|
|
||||||
var pNode = t
|
|
||||||
for _, lv := range topicSlice {
|
|
||||||
if _, ok := pNode.children[lv]; ok {
|
|
||||||
pNode = pNode.children[lv]
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pNode.topicName == topicFilter {
|
|
||||||
return pNode
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsubscribe
|
|
||||||
func (t *topicTrie) unsubscribe(clientID string, topicName string, shareName string) {
|
|
||||||
topicSlice := strings.Split(topicName, "/")
|
|
||||||
l := len(topicSlice)
|
|
||||||
var pNode = t
|
|
||||||
for _, lv := range topicSlice {
|
|
||||||
if _, ok := pNode.children[lv]; ok {
|
|
||||||
pNode = pNode.children[lv]
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if shareName != "" {
|
|
||||||
if c := pNode.shared[shareName]; c != nil {
|
|
||||||
delete(c, clientID)
|
|
||||||
if len(pNode.shared[shareName]) == 0 {
|
|
||||||
delete(pNode.shared, shareName)
|
|
||||||
}
|
|
||||||
if len(pNode.shared) == 0 && len(pNode.children) == 0 {
|
|
||||||
delete(pNode.parent.children, topicSlice[l-1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete(pNode.clients, clientID)
|
|
||||||
if len(pNode.clients) == 0 && len(pNode.children) == 0 {
|
|
||||||
delete(pNode.parent.children, topicSlice[l-1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// setRs set the node subscription info into rs
|
|
||||||
func setRs(node *topicNode, rs subscription.ClientSubscriptions) {
|
|
||||||
for cid, subOpts := range node.clients {
|
|
||||||
rs[cid] = append(rs[cid], subOpts)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range node.shared {
|
|
||||||
for cid, subOpts := range c {
|
|
||||||
rs[cid] = append(rs[cid], subOpts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// matchTopic get all matched topic for given topicSlice, and set into rs
|
|
||||||
func (t *topicTrie) matchTopic(topicSlice []string, rs subscription.ClientSubscriptions) {
|
|
||||||
endFlag := len(topicSlice) == 1
|
|
||||||
if cnode := t.children["#"]; cnode != nil {
|
|
||||||
setRs(cnode, rs)
|
|
||||||
}
|
|
||||||
if cnode := t.children["+"]; cnode != nil {
|
|
||||||
if endFlag {
|
|
||||||
setRs(cnode, rs)
|
|
||||||
if n := cnode.children["#"]; n != nil {
|
|
||||||
setRs(n, rs)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cnode.matchTopic(topicSlice[1:], rs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cnode := t.children[topicSlice[0]]; cnode != nil {
|
|
||||||
if endFlag {
|
|
||||||
setRs(cnode, rs)
|
|
||||||
if n := cnode.children["#"]; n != nil {
|
|
||||||
setRs(n, rs)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cnode.matchTopic(topicSlice[1:], rs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMatchedTopicFilter return a map key by clientID that contain all matched topic for the given topicName.
|
|
||||||
func (t *topicTrie) getMatchedTopicFilter(topicName string) subscription.ClientSubscriptions {
|
|
||||||
topicLv := strings.Split(topicName, "/")
|
|
||||||
subs := make(subscription.ClientSubscriptions)
|
|
||||||
t.matchTopic(topicLv, subs)
|
|
||||||
return subs
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSystemTopic(topicName string) bool {
|
|
||||||
return len(topicName) >= 1 && topicName[0] == '$'
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *topicTrie) preOrderTraverse(fn subscription.IterateFn) bool {
|
|
||||||
if t == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if t.topicName != "" {
|
|
||||||
for clientID, subOpts := range t.clients {
|
|
||||||
if !fn(clientID, subOpts) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range t.shared {
|
|
||||||
for clientID, subOpts := range c {
|
|
||||||
if !fn(clientID, subOpts) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, c := range t.children {
|
|
||||||
if !c.preOrderTraverse(fn) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,309 +0,0 @@
|
|||||||
package mem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
var testTopicMatch = []struct {
|
|
||||||
subTopic string //subscribe topic
|
|
||||||
topic string //publish topic
|
|
||||||
isMatch bool
|
|
||||||
}{
|
|
||||||
{subTopic: "#", topic: "/abc/def", isMatch: true},
|
|
||||||
{subTopic: "/a", topic: "a", isMatch: false},
|
|
||||||
{subTopic: "a/#", topic: "a", isMatch: true},
|
|
||||||
{subTopic: "+", topic: "/a", isMatch: false},
|
|
||||||
|
|
||||||
{subTopic: "a/", topic: "a", isMatch: false},
|
|
||||||
{subTopic: "a/+", topic: "a/123/4", isMatch: false},
|
|
||||||
{subTopic: "a/#", topic: "a/123/4", isMatch: true},
|
|
||||||
|
|
||||||
{subTopic: "/a/+/+/abcd", topic: "/a/dfdf/3434/abcd", isMatch: true},
|
|
||||||
{subTopic: "/a/+/+/abcd", topic: "/a/dfdf/3434/abcdd", isMatch: false},
|
|
||||||
{subTopic: "/a/+/abc/", topic: "/a/dfdf/abc/", isMatch: true},
|
|
||||||
{subTopic: "/a/+/abc/", topic: "/a/dfdf/abc", isMatch: false},
|
|
||||||
{subTopic: "/a/+/+/", topic: "/a/dfdf/", isMatch: false},
|
|
||||||
{subTopic: "/a/+/+", topic: "/a/dfdf/", isMatch: true},
|
|
||||||
{subTopic: "/a/+/+/#", topic: "/a/dfdf/", isMatch: true},
|
|
||||||
}
|
|
||||||
|
|
||||||
var topicMatchQosTest = []struct {
|
|
||||||
topics []packets.Topic
|
|
||||||
matchTopic struct {
|
|
||||||
name string // matched topic name
|
|
||||||
qos uint8 // matched qos
|
|
||||||
}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
topics: []packets.Topic{
|
|
||||||
{
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos1,
|
|
||||||
},
|
|
||||||
Name: "a/b",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "a/#",
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "a/+",
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
matchTopic: struct {
|
|
||||||
name string
|
|
||||||
qos uint8
|
|
||||||
}{
|
|
||||||
name: "a/b",
|
|
||||||
qos: packets.Qos2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var testSubscribeAndFind = struct {
|
|
||||||
subTopics map[string][]packets.Topic // subscription
|
|
||||||
findTopics map[string][]struct { //key by clientID
|
|
||||||
exist bool
|
|
||||||
topicName string
|
|
||||||
wantQos uint8
|
|
||||||
}
|
|
||||||
}{
|
|
||||||
subTopics: map[string][]packets.Topic{
|
|
||||||
"cid1": {
|
|
||||||
{
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos1,
|
|
||||||
}, Name: "t1/t2/+"},
|
|
||||||
{SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos2,
|
|
||||||
}, Name: "t1/t2/"},
|
|
||||||
{SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos0,
|
|
||||||
}, Name: "t1/t2/cid1"},
|
|
||||||
},
|
|
||||||
"cid2": {
|
|
||||||
{SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos2,
|
|
||||||
}, Name: "t1/t2/+"},
|
|
||||||
{SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos1,
|
|
||||||
}, Name: "t1/t2/"},
|
|
||||||
{SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos0,
|
|
||||||
}, Name: "t1/t2/cid2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
findTopics: map[string][]struct { //key by clientID
|
|
||||||
exist bool
|
|
||||||
topicName string
|
|
||||||
wantQos uint8
|
|
||||||
}{
|
|
||||||
"cid1": {
|
|
||||||
{exist: true, topicName: "t1/t2/+", wantQos: packets.Qos1},
|
|
||||||
{exist: true, topicName: "t1/t2/", wantQos: packets.Qos2},
|
|
||||||
{exist: false, topicName: "t1/t2/cid2"},
|
|
||||||
{exist: false, topicName: "t1/t2/cid3"},
|
|
||||||
},
|
|
||||||
"cid2": {
|
|
||||||
{exist: true, topicName: "t1/t2/+", wantQos: packets.Qos2},
|
|
||||||
{exist: true, topicName: "t1/t2/", wantQos: packets.Qos1},
|
|
||||||
{exist: false, topicName: "t1/t2/cid1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var testUnsubscribe = struct {
|
|
||||||
subTopics map[string][]packets.Topic //key by clientID
|
|
||||||
unsubscribe map[string][]string // clientID => topic name
|
|
||||||
afterUnsub map[string][]struct { // test after unsubscribe, key by clientID
|
|
||||||
exist bool
|
|
||||||
topicName string
|
|
||||||
wantQos uint8
|
|
||||||
}
|
|
||||||
}{
|
|
||||||
subTopics: map[string][]packets.Topic{
|
|
||||||
"cid1": {
|
|
||||||
{SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos1,
|
|
||||||
}, Name: "t1/t2/t3"},
|
|
||||||
{SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos2,
|
|
||||||
}, Name: "t1/t2"},
|
|
||||||
},
|
|
||||||
"cid2": {
|
|
||||||
{
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos2,
|
|
||||||
},
|
|
||||||
Name: "t1/t2/t3"},
|
|
||||||
{
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos1,
|
|
||||||
}, Name: "t1/t2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
unsubscribe: map[string][]string{
|
|
||||||
"cid1": {"t1/t2/t3", "t4/t5"},
|
|
||||||
"cid2": {"t1/t2/t3"},
|
|
||||||
},
|
|
||||||
afterUnsub: map[string][]struct { // test after unsubscribe
|
|
||||||
exist bool
|
|
||||||
topicName string
|
|
||||||
wantQos uint8
|
|
||||||
}{
|
|
||||||
"cid1": {
|
|
||||||
{exist: false, topicName: "t1/t2/t3"},
|
|
||||||
{exist: true, topicName: "t1/t2", wantQos: packets.Qos2},
|
|
||||||
},
|
|
||||||
"cid2": {
|
|
||||||
{exist: false, topicName: "t1/t2/+"},
|
|
||||||
{exist: true, topicName: "t1/t2", wantQos: packets.Qos1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var testPreOrderTraverse = struct {
|
|
||||||
topics []packets.Topic
|
|
||||||
clientID string
|
|
||||||
}{
|
|
||||||
topics: []packets.Topic{
|
|
||||||
{
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos0,
|
|
||||||
},
|
|
||||||
Name: "a/b/c",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos1,
|
|
||||||
},
|
|
||||||
Name: "/a/b/c",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: packets.Qos2,
|
|
||||||
},
|
|
||||||
Name: "b/c/d",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
clientID: "abc",
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTopicTrie_matchedClients(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
for _, v := range testTopicMatch {
|
|
||||||
trie := newTopicTrie()
|
|
||||||
trie.subscribe("cid", &gmqtt.Subscription{
|
|
||||||
TopicFilter: v.subTopic,
|
|
||||||
})
|
|
||||||
qos := trie.getMatchedTopicFilter(v.topic)
|
|
||||||
if v.isMatch {
|
|
||||||
a.EqualValues(qos["cid"][0].QoS, 0, v.subTopic)
|
|
||||||
} else {
|
|
||||||
_, ok := qos["cid"]
|
|
||||||
a.False(ok, v.subTopic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTopicTrie_matchedClients_Qos(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
for _, v := range topicMatchQosTest {
|
|
||||||
trie := newTopicTrie()
|
|
||||||
for _, tt := range v.topics {
|
|
||||||
trie.subscribe("cid", &gmqtt.Subscription{
|
|
||||||
TopicFilter: tt.Name,
|
|
||||||
QoS: tt.Qos,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
rs := trie.getMatchedTopicFilter(v.matchTopic.name)
|
|
||||||
a.EqualValues(v.matchTopic.qos, rs["cid"][0].QoS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTopicTrie_subscribeAndFind(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
trie := newTopicTrie()
|
|
||||||
for cid, v := range testSubscribeAndFind.subTopics {
|
|
||||||
for _, topic := range v {
|
|
||||||
trie.subscribe(cid, &gmqtt.Subscription{
|
|
||||||
TopicFilter: topic.Name,
|
|
||||||
QoS: topic.Qos,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for cid, v := range testSubscribeAndFind.findTopics {
|
|
||||||
for _, tt := range v {
|
|
||||||
node := trie.find(tt.topicName)
|
|
||||||
if tt.exist {
|
|
||||||
a.Equal(tt.wantQos, node.clients[cid].QoS)
|
|
||||||
} else {
|
|
||||||
if node != nil {
|
|
||||||
_, ok := node.clients[cid]
|
|
||||||
a.False(ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTopicTrie_unsubscribe(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
trie := newTopicTrie()
|
|
||||||
for cid, v := range testUnsubscribe.subTopics {
|
|
||||||
for _, topic := range v {
|
|
||||||
trie.subscribe(cid, &gmqtt.Subscription{
|
|
||||||
TopicFilter: topic.Name,
|
|
||||||
QoS: topic.Qos,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for cid, v := range testUnsubscribe.unsubscribe {
|
|
||||||
for _, tt := range v {
|
|
||||||
trie.unsubscribe(cid, tt, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for cid, v := range testUnsubscribe.afterUnsub {
|
|
||||||
for _, tt := range v {
|
|
||||||
matched := trie.getMatchedTopicFilter(tt.topicName)
|
|
||||||
if tt.exist {
|
|
||||||
a.EqualValues(matched[cid][0].QoS, tt.wantQos)
|
|
||||||
} else {
|
|
||||||
a.Equal(0, len(matched))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTopicTrie_preOrderTraverse(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
trie := newTopicTrie()
|
|
||||||
for _, v := range testPreOrderTraverse.topics {
|
|
||||||
trie.subscribe(testPreOrderTraverse.clientID, &gmqtt.Subscription{
|
|
||||||
TopicFilter: v.Name,
|
|
||||||
QoS: v.Qos,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
var rs []packets.Topic
|
|
||||||
trie.preOrderTraverse(func(clientID string, subscription *gmqtt.Subscription) bool {
|
|
||||||
a.Equal(testPreOrderTraverse.clientID, clientID)
|
|
||||||
rs = append(rs, packets.Topic{
|
|
||||||
SubOptions: packets.SubOptions{
|
|
||||||
Qos: subscription.QoS,
|
|
||||||
},
|
|
||||||
Name: subscription.TopicFilter,
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
a.ElementsMatch(testPreOrderTraverse.topics, rs)
|
|
||||||
}
|
|
@ -1,385 +0,0 @@
|
|||||||
package mem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ subscription.Store = (*TrieDB)(nil)
|
|
||||||
|
|
||||||
// TrieDB implement the subscription.Interface, it use trie tree to store topics.
|
|
||||||
type TrieDB struct {
|
|
||||||
sync.RWMutex
|
|
||||||
userIndex map[string]map[string]*topicNode // [clientID][topicFilter]
|
|
||||||
userTrie *topicTrie
|
|
||||||
|
|
||||||
// system topic which begin with "$"
|
|
||||||
systemIndex map[string]map[string]*topicNode // [clientID][topicFilter]
|
|
||||||
systemTrie *topicTrie
|
|
||||||
|
|
||||||
// shared subscription which begin with "$share"
|
|
||||||
sharedIndex map[string]map[string]*topicNode // [clientID][shareName/topicFilter]
|
|
||||||
sharedTrie *topicTrie
|
|
||||||
|
|
||||||
// statistics of the server and each client
|
|
||||||
stats subscription.Stats
|
|
||||||
clientStats map[string]*subscription.Stats // [clientID]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *TrieDB) Init(clientIDs []string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *TrieDB) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func iterateShared(fn subscription.IterateFn, options subscription.IterationOptions, index map[string]map[string]*topicNode, trie *topicTrie) bool {
|
|
||||||
// 查询指定topicFilter
|
|
||||||
if options.TopicName != "" && options.MatchType == subscription.MatchName { //寻找指定topicName
|
|
||||||
var shareName string
|
|
||||||
var topicFilter string
|
|
||||||
if strings.HasPrefix(options.TopicName, "$share/") {
|
|
||||||
shared := strings.SplitN(options.TopicName, "/", 3)
|
|
||||||
shareName = shared[1]
|
|
||||||
topicFilter = shared[2]
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
node := trie.find(topicFilter)
|
|
||||||
if node == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if options.ClientID != "" { // 指定topicName & 指定clientID
|
|
||||||
if c := node.shared[shareName]; c != nil {
|
|
||||||
if sub, ok := c[options.ClientID]; ok {
|
|
||||||
if !fn(options.ClientID, sub) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if c := node.shared[shareName]; c != nil {
|
|
||||||
for clientID, sub := range c {
|
|
||||||
if !fn(clientID, sub) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// 查询Match指定topicFilter
|
|
||||||
if options.TopicName != "" && options.MatchType == subscription.MatchFilter { // match指定的topicfilter
|
|
||||||
node := trie.getMatchedTopicFilter(options.TopicName)
|
|
||||||
if node == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if options.ClientID != "" {
|
|
||||||
for _, v := range node[options.ClientID] {
|
|
||||||
if !fn(options.ClientID, v) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for clientID, subs := range node {
|
|
||||||
for _, v := range subs {
|
|
||||||
if !fn(clientID, v) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// 查询指定clientID下的所有topic
|
|
||||||
if options.ClientID != "" {
|
|
||||||
for _, v := range index[options.ClientID] {
|
|
||||||
for _, c := range v.shared {
|
|
||||||
if sub, ok := c[options.ClientID]; ok {
|
|
||||||
if !fn(options.ClientID, sub) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// 遍历
|
|
||||||
return trie.preOrderTraverse(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func iterateNonShared(fn subscription.IterateFn, options subscription.IterationOptions, index map[string]map[string]*topicNode, trie *topicTrie) bool {
|
|
||||||
// 查询指定topicFilter
|
|
||||||
if options.TopicName != "" && options.MatchType == subscription.MatchName { //寻找指定topicName
|
|
||||||
node := trie.find(options.TopicName)
|
|
||||||
if node == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if options.ClientID != "" { // 指定topicName & 指定clientID
|
|
||||||
if sub, ok := node.clients[options.ClientID]; ok {
|
|
||||||
if !fn(options.ClientID, sub) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range node.shared {
|
|
||||||
if sub, ok := v[options.ClientID]; ok {
|
|
||||||
if !fn(options.ClientID, sub) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// 指定topic name 不指定clientid
|
|
||||||
for clientID, sub := range node.clients {
|
|
||||||
if !fn(clientID, sub) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, c := range node.shared {
|
|
||||||
for clientID, sub := range c {
|
|
||||||
if !fn(clientID, sub) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// 查询Match指定topicFilter
|
|
||||||
if options.TopicName != "" && options.MatchType == subscription.MatchFilter { // match指定的topicfilter
|
|
||||||
node := trie.getMatchedTopicFilter(options.TopicName)
|
|
||||||
if node == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if options.ClientID != "" {
|
|
||||||
for _, v := range node[options.ClientID] {
|
|
||||||
if !fn(options.ClientID, v) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for clientID, subs := range node {
|
|
||||||
for _, v := range subs {
|
|
||||||
if !fn(clientID, v) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// 查询指定clientID下的所有topic
|
|
||||||
if options.ClientID != "" {
|
|
||||||
for _, v := range index[options.ClientID] {
|
|
||||||
sub := v.clients[options.ClientID]
|
|
||||||
if !fn(options.ClientID, sub) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// 遍历
|
|
||||||
return trie.preOrderTraverse(fn)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// IterateLocked is the non thread-safe version of Iterate
|
|
||||||
func (db *TrieDB) IterateLocked(fn subscription.IterateFn, options subscription.IterationOptions) {
|
|
||||||
if options.Type&subscription.TypeShared == subscription.TypeShared {
|
|
||||||
if !iterateShared(fn, options, db.sharedIndex, db.sharedTrie) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if options.Type&subscription.TypeNonShared == subscription.TypeNonShared {
|
|
||||||
// The Server MUST NOT match Topic Filters starting with a wildcard character (# or +) with Topic Names beginning with a $ character [MQTT-4.7.2-1]
|
|
||||||
if !(options.TopicName != "" && isSystemTopic(options.TopicName)) {
|
|
||||||
if !iterateNonShared(fn, options, db.userIndex, db.userTrie) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if options.Type&subscription.TypeSYS == subscription.TypeSYS {
|
|
||||||
if options.TopicName != "" && !isSystemTopic(options.TopicName) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !iterateNonShared(fn, options, db.systemIndex, db.systemTrie) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (db *TrieDB) Iterate(fn subscription.IterateFn, options subscription.IterationOptions) {
|
|
||||||
db.RLock()
|
|
||||||
defer db.RUnlock()
|
|
||||||
db.IterateLocked(fn, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStats is the non thread-safe version of GetStats
|
|
||||||
func (db *TrieDB) GetStatusLocked() subscription.Stats {
|
|
||||||
return db.stats
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStats returns the statistic information of the store
|
|
||||||
func (db *TrieDB) GetStats() subscription.Stats {
|
|
||||||
db.RLock()
|
|
||||||
defer db.RUnlock()
|
|
||||||
return db.GetStatusLocked()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientStatsLocked the non thread-safe version of GetClientStats
|
|
||||||
func (db *TrieDB) GetClientStatsLocked(clientID string) (subscription.Stats, error) {
|
|
||||||
if stats, ok := db.clientStats[clientID]; !ok {
|
|
||||||
return subscription.Stats{}, subscription.ErrClientNotExists
|
|
||||||
} else {
|
|
||||||
return *stats, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *TrieDB) GetClientStats(clientID string) (subscription.Stats, error) {
|
|
||||||
db.RLock()
|
|
||||||
defer db.RUnlock()
|
|
||||||
return db.GetClientStatsLocked(clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStore create a new TrieDB instance
|
|
||||||
func NewStore() *TrieDB {
|
|
||||||
return &TrieDB{
|
|
||||||
userIndex: make(map[string]map[string]*topicNode),
|
|
||||||
userTrie: newTopicTrie(),
|
|
||||||
|
|
||||||
systemIndex: make(map[string]map[string]*topicNode),
|
|
||||||
systemTrie: newTopicTrie(),
|
|
||||||
|
|
||||||
sharedIndex: make(map[string]map[string]*topicNode),
|
|
||||||
sharedTrie: newTopicTrie(),
|
|
||||||
|
|
||||||
clientStats: make(map[string]*subscription.Stats),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeLocked is the non thread-safe version of Subscribe
|
|
||||||
func (db *TrieDB) SubscribeLocked(clientID string, subscriptions ...*gmqtt.Subscription) subscription.SubscribeResult {
|
|
||||||
var node *topicNode
|
|
||||||
var index map[string]map[string]*topicNode
|
|
||||||
rs := make(subscription.SubscribeResult, len(subscriptions))
|
|
||||||
for k, sub := range subscriptions {
|
|
||||||
topicName := sub.TopicFilter
|
|
||||||
rs[k].Subscription = sub
|
|
||||||
if sub.ShareName != "" {
|
|
||||||
node = db.sharedTrie.subscribe(clientID, sub)
|
|
||||||
index = db.sharedIndex
|
|
||||||
} else if isSystemTopic(topicName) {
|
|
||||||
node = db.systemTrie.subscribe(clientID, sub)
|
|
||||||
index = db.systemIndex
|
|
||||||
} else {
|
|
||||||
node = db.userTrie.subscribe(clientID, sub)
|
|
||||||
index = db.userIndex
|
|
||||||
}
|
|
||||||
if index[clientID] == nil {
|
|
||||||
index[clientID] = make(map[string]*topicNode)
|
|
||||||
if db.clientStats[clientID] == nil {
|
|
||||||
db.clientStats[clientID] = &subscription.Stats{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := index[clientID][topicName]; !ok {
|
|
||||||
db.stats.SubscriptionsTotal++
|
|
||||||
db.stats.SubscriptionsCurrent++
|
|
||||||
db.clientStats[clientID].SubscriptionsTotal++
|
|
||||||
db.clientStats[clientID].SubscriptionsCurrent++
|
|
||||||
} else {
|
|
||||||
rs[k].AlreadyExisted = true
|
|
||||||
}
|
|
||||||
index[clientID][topicName] = node
|
|
||||||
}
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeLocked add subscriptions for the client
|
|
||||||
func (db *TrieDB) Subscribe(clientID string, subscriptions ...*gmqtt.Subscription) (subscription.SubscribeResult, error) {
|
|
||||||
db.Lock()
|
|
||||||
defer db.Unlock()
|
|
||||||
return db.SubscribeLocked(clientID, subscriptions...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsubscribeLocked is the non thread-safe version of Unsubscribe
|
|
||||||
func (db *TrieDB) UnsubscribeLocked(clientID string, topics ...string) {
|
|
||||||
var index map[string]map[string]*topicNode
|
|
||||||
var topicTrie *topicTrie
|
|
||||||
for _, topic := range topics {
|
|
||||||
var shareName string
|
|
||||||
shareName, topic := subscription.SplitTopic(topic)
|
|
||||||
if shareName != "" {
|
|
||||||
topicTrie = db.sharedTrie
|
|
||||||
index = db.sharedIndex
|
|
||||||
} else if isSystemTopic(topic) {
|
|
||||||
index = db.systemIndex
|
|
||||||
topicTrie = db.systemTrie
|
|
||||||
} else {
|
|
||||||
index = db.userIndex
|
|
||||||
topicTrie = db.userTrie
|
|
||||||
}
|
|
||||||
if _, ok := index[clientID]; ok {
|
|
||||||
if _, ok := index[clientID][topic]; ok {
|
|
||||||
db.stats.SubscriptionsCurrent--
|
|
||||||
db.clientStats[clientID].SubscriptionsCurrent--
|
|
||||||
}
|
|
||||||
delete(index[clientID], topic)
|
|
||||||
}
|
|
||||||
topicTrie.unsubscribe(clientID, topic, shareName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe remove subscriptions for the client
|
|
||||||
func (db *TrieDB) Unsubscribe(clientID string, topics ...string) error {
|
|
||||||
db.Lock()
|
|
||||||
defer db.Unlock()
|
|
||||||
db.UnsubscribeLocked(clientID, topics...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *TrieDB) unsubscribeAll(index map[string]map[string]*topicNode, clientID string) {
|
|
||||||
db.stats.SubscriptionsCurrent -= uint64(len(index[clientID]))
|
|
||||||
if db.clientStats[clientID] != nil {
|
|
||||||
db.clientStats[clientID].SubscriptionsCurrent -= uint64(len(index[clientID]))
|
|
||||||
}
|
|
||||||
for topicName, node := range index[clientID] {
|
|
||||||
delete(node.clients, clientID)
|
|
||||||
if len(node.clients) == 0 && len(node.children) == 0 {
|
|
||||||
ss := strings.Split(topicName, "/")
|
|
||||||
delete(node.parent.children, ss[len(ss)-1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete(index, clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsubscribeAllLocked is the non thread-safe version of UnsubscribeAll
|
|
||||||
func (db *TrieDB) UnsubscribeAllLocked(clientID string) {
|
|
||||||
db.unsubscribeAll(db.userIndex, clientID)
|
|
||||||
db.unsubscribeAll(db.systemIndex, clientID)
|
|
||||||
db.unsubscribeAll(db.sharedIndex, clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsubscribeAll delete all subscriptions of the client
|
|
||||||
func (db *TrieDB) UnsubscribeAll(clientID string) error {
|
|
||||||
db.Lock()
|
|
||||||
defer db.Unlock()
|
|
||||||
// user topics
|
|
||||||
db.UnsubscribeAllLocked(clientID)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMatchedTopicFilter return a map key by clientID that contain all matched topic for the given topicName.
|
|
||||||
func (db *TrieDB) getMatchedTopicFilter(topicName string) subscription.ClientSubscriptions {
|
|
||||||
// system topic
|
|
||||||
if isSystemTopic(topicName) {
|
|
||||||
return db.systemTrie.getMatchedTopicFilter(topicName)
|
|
||||||
}
|
|
||||||
return db.userTrie.getMatchedTopicFilter(topicName)
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
redigo "github.com/gomodule/redigo/redis"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/encoding"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription/mem"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
subPrefix = "sub:"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ subscription.Store = (*sub)(nil)
|
|
||||||
|
|
||||||
func EncodeSubscription(sub *mqttbroker.Subscription) []byte {
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
encoding.WriteString(w, []byte(sub.ShareName))
|
|
||||||
encoding.WriteString(w, []byte(sub.TopicFilter))
|
|
||||||
encoding.WriteUint32(w, sub.ID)
|
|
||||||
w.WriteByte(sub.QoS)
|
|
||||||
encoding.WriteBool(w, sub.NoLocal)
|
|
||||||
encoding.WriteBool(w, sub.RetainAsPublished)
|
|
||||||
w.WriteByte(sub.RetainHandling)
|
|
||||||
return w.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecodeSubscription(b []byte) (*gmqtt.Subscription, error) {
|
|
||||||
sub := &gmqtt.Subscription{}
|
|
||||||
r := bytes.NewBuffer(b)
|
|
||||||
share, err := encoding.ReadString(r)
|
|
||||||
if err != nil {
|
|
||||||
return &gmqtt.Subscription{}, err
|
|
||||||
}
|
|
||||||
sub.ShareName = string(share)
|
|
||||||
topic, err := encoding.ReadString(r)
|
|
||||||
if err != nil {
|
|
||||||
return &gmqtt.Subscription{}, err
|
|
||||||
}
|
|
||||||
sub.TopicFilter = string(topic)
|
|
||||||
sub.ID, err = encoding.ReadUint32(r)
|
|
||||||
if err != nil {
|
|
||||||
return &gmqtt.Subscription{}, err
|
|
||||||
}
|
|
||||||
sub.QoS, err = r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return &gmqtt.Subscription{}, err
|
|
||||||
}
|
|
||||||
sub.NoLocal, err = encoding.ReadBool(r)
|
|
||||||
if err != nil {
|
|
||||||
return &gmqtt.Subscription{}, err
|
|
||||||
}
|
|
||||||
sub.RetainAsPublished, err = encoding.ReadBool(r)
|
|
||||||
if err != nil {
|
|
||||||
return &gmqtt.Subscription{}, err
|
|
||||||
}
|
|
||||||
sub.RetainHandling, err = r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return sub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(pool *redigo.Pool) *sub {
|
|
||||||
return &sub{
|
|
||||||
mu: &sync.Mutex{},
|
|
||||||
memStore: mem.NewStore(),
|
|
||||||
pool: pool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type sub struct {
|
|
||||||
mu *sync.Mutex
|
|
||||||
memStore *mem.TrieDB
|
|
||||||
pool *redigo.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init loads the subscriptions of given clientIDs from backend into memory.
|
|
||||||
func (s *sub) Init(clientIDs []string) error {
|
|
||||||
if len(clientIDs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
for _, v := range clientIDs {
|
|
||||||
rs, err := redigo.Values(c.Do("hgetall", subPrefix+v))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i := 1; i < len(rs); i = i + 2 {
|
|
||||||
sub, err := DecodeSubscription(rs[i].([]byte))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.memStore.SubscribeLocked(strings.TrimLeft(v, subPrefix), sub)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sub) Close() error {
|
|
||||||
_ = s.memStore.Close()
|
|
||||||
return s.pool.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sub) Subscribe(clientID string, subscriptions ...*gmqtt.Subscription) (rs subscription.SubscribeResult, err error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
// hset sub:clientID topicFilter xxx
|
|
||||||
for _, v := range subscriptions {
|
|
||||||
err = c.Send("hset", subPrefix+clientID, subscription.GetFullTopicName(v.ShareName, v.TopicFilter), EncodeSubscription(v))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = c.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rs = s.memStore.SubscribeLocked(clientID, subscriptions...)
|
|
||||||
return rs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sub) Unsubscribe(clientID string, topics ...string) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
_, err := c.Do("hdel", subPrefix+clientID, topics)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.memStore.UnsubscribeLocked(clientID, topics...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sub) UnsubscribeAll(clientID string) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
_, err := c.Do("del", subPrefix+clientID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.memStore.UnsubscribeAllLocked(clientID)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sub) Iterate(fn subscription.IterateFn, options subscription.IterationOptions) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
s.memStore.IterateLocked(fn, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sub) GetStats() subscription.Stats {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
return s.memStore.GetStatusLocked()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sub) GetClientStats(clientID string) (subscription.Stats, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
return s.memStore.GetClientStatsLocked(clientID)
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodeDecodeSubscription(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
tt := []*mqttbroker.Subscription{
|
|
||||||
{
|
|
||||||
ShareName: "shareName",
|
|
||||||
TopicFilter: "filter",
|
|
||||||
ID: 1,
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: false,
|
|
||||||
RetainAsPublished: false,
|
|
||||||
RetainHandling: 0,
|
|
||||||
}, {
|
|
||||||
ShareName: "",
|
|
||||||
TopicFilter: "abc",
|
|
||||||
ID: 0,
|
|
||||||
QoS: 2,
|
|
||||||
NoLocal: false,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range tt {
|
|
||||||
b := EncodeSubscription(v)
|
|
||||||
sub, err := DecodeSubscription(b)
|
|
||||||
a.Nil(err)
|
|
||||||
a.Equal(v, sub)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,193 +0,0 @@
|
|||||||
package subscription
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IterationType specifies the types of subscription that will be iterated.
|
|
||||||
type IterationType byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
// TypeSYS represents system topic, which start with '$'.
|
|
||||||
TypeSYS IterationType = 1 << iota
|
|
||||||
// TypeSYS represents shared topic, which start with '$share/'.
|
|
||||||
TypeShared
|
|
||||||
// TypeNonShared represents non-shared topic.
|
|
||||||
TypeNonShared
|
|
||||||
TypeAll = TypeSYS | TypeShared | TypeNonShared
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrClientNotExists = errors.New("client not exists")
|
|
||||||
)
|
|
||||||
|
|
||||||
// MatchType specifies what match operation will be performed during the iteration.
|
|
||||||
type MatchType byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
MatchName MatchType = 1 << iota
|
|
||||||
MatchFilter
|
|
||||||
)
|
|
||||||
|
|
||||||
// FromTopic returns the subscription instance for given topic and subscription id.
|
|
||||||
func FromTopic(topic packets.Topic, id uint32) *gmqtt.Subscription {
|
|
||||||
shareName, topicFilter := SplitTopic(topic.Name)
|
|
||||||
s := &gmqtt.Subscription{
|
|
||||||
ShareName: shareName,
|
|
||||||
TopicFilter: topicFilter,
|
|
||||||
ID: id,
|
|
||||||
QoS: topic.Qos,
|
|
||||||
NoLocal: topic.NoLocal,
|
|
||||||
RetainAsPublished: topic.RetainAsPublished,
|
|
||||||
RetainHandling: topic.RetainHandling,
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// IterateFn is the callback function used by iterate()
|
|
||||||
// Return false means to stop the iteration.
|
|
||||||
type IterateFn func(clientID string, sub *gmqtt.Subscription) bool
|
|
||||||
|
|
||||||
// SubscribeResult is the result of Subscribe()
|
|
||||||
type SubscribeResult = []struct {
|
|
||||||
// Topic is the Subscribed topic
|
|
||||||
Subscription *gmqtt.Subscription
|
|
||||||
// AlreadyExisted shows whether the topic is already existed.
|
|
||||||
AlreadyExisted bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stats is the statistics information of the store
|
|
||||||
type Stats struct {
|
|
||||||
// SubscriptionsTotal shows how many subscription has been added to the store.
|
|
||||||
// Duplicated subscription is not counting.
|
|
||||||
SubscriptionsTotal uint64
|
|
||||||
// SubscriptionsCurrent shows the current subscription number in the store.
|
|
||||||
SubscriptionsCurrent uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientSubscriptions groups the subscriptions by client id.
|
|
||||||
type ClientSubscriptions map[string][]*gmqtt.Subscription
|
|
||||||
|
|
||||||
// IterationOptions
|
|
||||||
type IterationOptions struct {
|
|
||||||
// Type specifies the types of subscription that will be iterated.
|
|
||||||
// For example, if Type = TypeShared | TypeNonShared , then all shared and non-shared subscriptions will be iterated
|
|
||||||
Type IterationType
|
|
||||||
// ClientID specifies the subscriber client id.
|
|
||||||
ClientID string
|
|
||||||
// TopicName represents topic filter or topic name. This field works together with MatchType.
|
|
||||||
TopicName string
|
|
||||||
// MatchType specifies the matching type of the iteration.
|
|
||||||
// if MatchName, the IterateFn will be called when the subscription topic filter is equal to TopicName.
|
|
||||||
// if MatchTopic, the IterateFn will be called when the TopicName match the subscription topic filter.
|
|
||||||
MatchType MatchType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store is the interface used by gmqtt.server to handler the operations of subscriptions.
|
|
||||||
// This interface provides the ability for extensions to interact with the subscriptions.
|
|
||||||
// Notice:
|
|
||||||
// This methods will not trigger any gmqtt hooks.
|
|
||||||
type Store interface {
|
|
||||||
// Init will be called only once after the server start, the implementation should load the subscriptions of the given alertclient into memory.
|
|
||||||
Init(clientIDs []string) error
|
|
||||||
// Subscribe adds subscriptions to a specific client.
|
|
||||||
// Notice:
|
|
||||||
// This method will succeed even if the client is not exists, the subscriptions
|
|
||||||
// will affect the new client with the client id.
|
|
||||||
Subscribe(clientID string, subscriptions ...*gmqtt.Subscription) (rs SubscribeResult, err error)
|
|
||||||
// Unsubscribe removes subscriptions of a specific client.
|
|
||||||
Unsubscribe(clientID string, topics ...string) error
|
|
||||||
// UnsubscribeAll removes all subscriptions of a specific client.
|
|
||||||
UnsubscribeAll(clientID string) error
|
|
||||||
// Iterate iterates all subscriptions. The callback is called once for each subscription.
|
|
||||||
// If callback return false, the iteration will be stopped.
|
|
||||||
// Notice:
|
|
||||||
// The results are not sorted in any way, no ordering of any kind is guaranteed.
|
|
||||||
// This method will walk through all subscriptions,
|
|
||||||
// so it is a very expensive operation. Do not call it frequently.
|
|
||||||
Iterate(fn IterateFn, options IterationOptions)
|
|
||||||
|
|
||||||
Close() error
|
|
||||||
StatsReader
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTopicMatched returns the subscriptions that match the passed topic.
|
|
||||||
func GetTopicMatched(store Store, topicFilter string, t IterationType) ClientSubscriptions {
|
|
||||||
rs := make(ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, subscription *gmqtt.Subscription) bool {
|
|
||||||
rs[clientID] = append(rs[clientID], subscription)
|
|
||||||
return true
|
|
||||||
}, IterationOptions{
|
|
||||||
Type: t,
|
|
||||||
TopicName: topicFilter,
|
|
||||||
MatchType: MatchFilter,
|
|
||||||
})
|
|
||||||
if len(rs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the subscriptions that equals the passed topic filter.
|
|
||||||
func Get(store Store, topicFilter string, t IterationType) ClientSubscriptions {
|
|
||||||
rs := make(ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, subscription *gmqtt.Subscription) bool {
|
|
||||||
rs[clientID] = append(rs[clientID], subscription)
|
|
||||||
return true
|
|
||||||
}, IterationOptions{
|
|
||||||
Type: t,
|
|
||||||
TopicName: topicFilter,
|
|
||||||
MatchType: MatchName,
|
|
||||||
})
|
|
||||||
if len(rs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientSubscriptions returns the subscriptions of a specific client.
|
|
||||||
func GetClientSubscriptions(store Store, clientID string, t IterationType) []*gmqtt.Subscription {
|
|
||||||
var rs []*gmqtt.Subscription
|
|
||||||
store.Iterate(func(clientID string, subscription *gmqtt.Subscription) bool {
|
|
||||||
rs = append(rs, subscription)
|
|
||||||
return true
|
|
||||||
}, IterationOptions{
|
|
||||||
Type: t,
|
|
||||||
ClientID: clientID,
|
|
||||||
})
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatsReader provides the ability to get statistics information.
|
|
||||||
type StatsReader interface {
|
|
||||||
// GetStats return the global stats.
|
|
||||||
GetStats() Stats
|
|
||||||
// GetClientStats return the stats of a specific client.
|
|
||||||
// If stats not exists, return an error.
|
|
||||||
GetClientStats(clientID string) (Stats, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SplitTopic returns the shareName and topicFilter of the given topic.
|
|
||||||
// If the topic is invalid, returns empty strings.
|
|
||||||
func SplitTopic(topic string) (shareName, topicFilter string) {
|
|
||||||
if strings.HasPrefix(topic, "$share/") {
|
|
||||||
shared := strings.SplitN(topic, "/", 3)
|
|
||||||
if len(shared) < 3 {
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
return shared[1], shared[2]
|
|
||||||
}
|
|
||||||
return "", topic
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFullTopicName returns the full topic name of given shareName and topicFilter
|
|
||||||
func GetFullTopicName(shareName, topicFilter string) string {
|
|
||||||
if shareName != "" {
|
|
||||||
return "$share/" + shareName + "/" + topicFilter
|
|
||||||
}
|
|
||||||
return topicFilter
|
|
||||||
}
|
|
@ -1,209 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: persistence/subscription/subscription.go
|
|
||||||
|
|
||||||
// Package subscription is a generated GoMock package.
|
|
||||||
package subscription
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockStore is a mock of Store interface
|
|
||||||
type MockStore struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockStoreMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockStoreMockRecorder is the mock recorder for MockStore
|
|
||||||
type MockStoreMockRecorder struct {
|
|
||||||
mock *MockStore
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockStore creates a new mock instance
|
|
||||||
func NewMockStore(ctrl *gomock.Controller) *MockStore {
|
|
||||||
mock := &MockStore{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockStoreMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *MockStore) EXPECT() *MockStoreMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init mocks base method
|
|
||||||
func (m *MockStore) Init(clientIDs []string) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Init", clientIDs)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init indicates an expected call of Init
|
|
||||||
func (mr *MockStoreMockRecorder) Init(clientIDs interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockStore)(nil).Init), clientIDs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe mocks base method
|
|
||||||
func (m *MockStore) Subscribe(clientID string, subscriptions ...*gmqtt.Subscription) (SubscribeResult, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
varargs := []interface{}{clientID}
|
|
||||||
for _, a := range subscriptions {
|
|
||||||
varargs = append(varargs, a)
|
|
||||||
}
|
|
||||||
ret := m.ctrl.Call(m, "Subscribe", varargs...)
|
|
||||||
ret0, _ := ret[0].(SubscribeResult)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe indicates an expected call of Subscribe
|
|
||||||
func (mr *MockStoreMockRecorder) Subscribe(clientID interface{}, subscriptions ...interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
varargs := append([]interface{}{clientID}, subscriptions...)
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockStore)(nil).Subscribe), varargs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe mocks base method
|
|
||||||
func (m *MockStore) Unsubscribe(clientID string, topics ...string) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
varargs := []interface{}{clientID}
|
|
||||||
for _, a := range topics {
|
|
||||||
varargs = append(varargs, a)
|
|
||||||
}
|
|
||||||
ret := m.ctrl.Call(m, "Unsubscribe", varargs...)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe indicates an expected call of Unsubscribe
|
|
||||||
func (mr *MockStoreMockRecorder) Unsubscribe(clientID interface{}, topics ...interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
varargs := append([]interface{}{clientID}, topics...)
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockStore)(nil).Unsubscribe), varargs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsubscribeAll mocks base method
|
|
||||||
func (m *MockStore) UnsubscribeAll(clientID string) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "UnsubscribeAll", clientID)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsubscribeAll indicates an expected call of UnsubscribeAll
|
|
||||||
func (mr *MockStoreMockRecorder) UnsubscribeAll(clientID interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsubscribeAll", reflect.TypeOf((*MockStore)(nil).UnsubscribeAll), clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate mocks base method
|
|
||||||
func (m *MockStore) Iterate(fn IterateFn, options IterationOptions) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "Iterate", fn, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate indicates an expected call of Iterate
|
|
||||||
func (mr *MockStoreMockRecorder) Iterate(fn, options interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iterate", reflect.TypeOf((*MockStore)(nil).Iterate), fn, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close mocks base method
|
|
||||||
func (m *MockStore) Close() error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Close")
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close indicates an expected call of Close
|
|
||||||
func (mr *MockStoreMockRecorder) Close() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStore)(nil).Close))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStats mocks base method
|
|
||||||
func (m *MockStore) GetStats() Stats {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetStats")
|
|
||||||
ret0, _ := ret[0].(Stats)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStats indicates an expected call of GetStats
|
|
||||||
func (mr *MockStoreMockRecorder) GetStats() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStats", reflect.TypeOf((*MockStore)(nil).GetStats))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientStats mocks base method
|
|
||||||
func (m *MockStore) GetClientStats(clientID string) (Stats, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetClientStats", clientID)
|
|
||||||
ret0, _ := ret[0].(Stats)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientStats indicates an expected call of GetClientStats
|
|
||||||
func (mr *MockStoreMockRecorder) GetClientStats(clientID interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClientStats", reflect.TypeOf((*MockStore)(nil).GetClientStats), clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockStatsReader is a mock of StatsReader interface
|
|
||||||
type MockStatsReader struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockStatsReaderMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockStatsReaderMockRecorder is the mock recorder for MockStatsReader
|
|
||||||
type MockStatsReaderMockRecorder struct {
|
|
||||||
mock *MockStatsReader
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockStatsReader creates a new mock instance
|
|
||||||
func NewMockStatsReader(ctrl *gomock.Controller) *MockStatsReader {
|
|
||||||
mock := &MockStatsReader{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockStatsReaderMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *MockStatsReader) EXPECT() *MockStatsReaderMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStats mocks base method
|
|
||||||
func (m *MockStatsReader) GetStats() Stats {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetStats")
|
|
||||||
ret0, _ := ret[0].(Stats)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStats indicates an expected call of GetStats
|
|
||||||
func (mr *MockStatsReaderMockRecorder) GetStats() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStats", reflect.TypeOf((*MockStatsReader)(nil).GetStats))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientStats mocks base method
|
|
||||||
func (m *MockStatsReader) GetClientStats(clientID string) (Stats, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetClientStats", clientID)
|
|
||||||
ret0, _ := ret[0].(Stats)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientStats indicates an expected call of GetClientStats
|
|
||||||
func (mr *MockStatsReaderMockRecorder) GetClientStats(clientID interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClientStats", reflect.TypeOf((*MockStatsReader)(nil).GetClientStats), clientID)
|
|
||||||
}
|
|
@ -1,601 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
topicA = &gmqtt.Subscription{
|
|
||||||
TopicFilter: "topic/A",
|
|
||||||
ID: 1,
|
|
||||||
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
topicB = &gmqtt.Subscription{
|
|
||||||
TopicFilter: "topic/B",
|
|
||||||
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: false,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
systemTopicA = &gmqtt.Subscription{
|
|
||||||
TopicFilter: "$topic/A",
|
|
||||||
ID: 1,
|
|
||||||
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
systemTopicB = &gmqtt.Subscription{
|
|
||||||
TopicFilter: "$topic/B",
|
|
||||||
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: false,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedTopicA1 = &gmqtt.Subscription{
|
|
||||||
ShareName: "name1",
|
|
||||||
TopicFilter: "topic/A",
|
|
||||||
ID: 1,
|
|
||||||
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedTopicB1 = &gmqtt.Subscription{
|
|
||||||
ShareName: "name1",
|
|
||||||
TopicFilter: "topic/B",
|
|
||||||
ID: 1,
|
|
||||||
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedTopicA2 = &gmqtt.Subscription{
|
|
||||||
ShareName: "name2",
|
|
||||||
TopicFilter: "topic/A",
|
|
||||||
ID: 1,
|
|
||||||
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedTopicB2 = &gmqtt.Subscription{
|
|
||||||
ShareName: "name2",
|
|
||||||
TopicFilter: "topic/B",
|
|
||||||
ID: 1,
|
|
||||||
|
|
||||||
QoS: 1,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 1,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var testSubs = []struct {
|
|
||||||
clientID string
|
|
||||||
subs []*gmqtt.Subscription
|
|
||||||
}{
|
|
||||||
// non-share and non-system subscription
|
|
||||||
{
|
|
||||||
|
|
||||||
clientID: "client1",
|
|
||||||
subs: []*gmqtt.Subscription{
|
|
||||||
topicA, topicB,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
clientID: "client2",
|
|
||||||
subs: []*gmqtt.Subscription{
|
|
||||||
topicA, topicB,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// system subscription
|
|
||||||
{
|
|
||||||
|
|
||||||
clientID: "client1",
|
|
||||||
subs: []*gmqtt.Subscription{
|
|
||||||
systemTopicA, systemTopicB,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
|
|
||||||
clientID: "client2",
|
|
||||||
subs: []*gmqtt.Subscription{
|
|
||||||
systemTopicA, systemTopicB,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// share subscription
|
|
||||||
{
|
|
||||||
clientID: "client1",
|
|
||||||
subs: []*gmqtt.Subscription{
|
|
||||||
sharedTopicA1, sharedTopicB1, sharedTopicA2, sharedTopicB2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
clientID: "client2",
|
|
||||||
subs: []*gmqtt.Subscription{
|
|
||||||
sharedTopicA1, sharedTopicB1, sharedTopicA2, sharedTopicB2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAddSubscribe(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
for _, v := range testSubs {
|
|
||||||
_, err := store.Subscribe(v.clientID, v.subs...)
|
|
||||||
a.Nil(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGetStatus(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
var err error
|
|
||||||
tt := []struct {
|
|
||||||
clientID string
|
|
||||||
topic packets.Topic
|
|
||||||
}{
|
|
||||||
{clientID: "id0", topic: packets.Topic{Name: "name0", SubOptions: packets.SubOptions{Qos: packets.Qos0}}},
|
|
||||||
{clientID: "id1", topic: packets.Topic{Name: "name1", SubOptions: packets.SubOptions{Qos: packets.Qos1}}},
|
|
||||||
{clientID: "id2", topic: packets.Topic{Name: "name2", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
{clientID: "id3", topic: packets.Topic{Name: "name3", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
{clientID: "id4", topic: packets.Topic{Name: "name3", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
{clientID: "id4", topic: packets.Topic{Name: "name4", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
// test $share and system topic
|
|
||||||
{clientID: "id4", topic: packets.Topic{Name: "$share/abc/name4", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
{clientID: "id4", topic: packets.Topic{Name: "$SYS/abc/def", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
_, err = store.Subscribe(v.clientID, subscription.FromTopic(v.topic, 0))
|
|
||||||
a.NoError(err)
|
|
||||||
}
|
|
||||||
stats := store.GetStats()
|
|
||||||
expectedTotal, expectedCurrent := len(tt), len(tt)
|
|
||||||
|
|
||||||
a.EqualValues(expectedTotal, stats.SubscriptionsTotal)
|
|
||||||
a.EqualValues(expectedCurrent, stats.SubscriptionsCurrent)
|
|
||||||
|
|
||||||
// If subscribe duplicated topic, total and current statistics should not increase
|
|
||||||
_, err = store.Subscribe("id0", subscription.FromTopic(packets.Topic{SubOptions: packets.SubOptions{Qos: packets.Qos0}, Name: "name0"}, 0))
|
|
||||||
a.NoError(err)
|
|
||||||
_, err = store.Subscribe("id4", subscription.FromTopic(packets.Topic{SubOptions: packets.SubOptions{Qos: packets.Qos2}, Name: "$share/abc/name4"}, 0))
|
|
||||||
a.NoError(err)
|
|
||||||
|
|
||||||
stats = store.GetStats()
|
|
||||||
a.EqualValues(expectedTotal, stats.SubscriptionsTotal)
|
|
||||||
a.EqualValues(expectedCurrent, stats.SubscriptionsCurrent)
|
|
||||||
|
|
||||||
utt := []struct {
|
|
||||||
clientID string
|
|
||||||
topic packets.Topic
|
|
||||||
}{
|
|
||||||
{clientID: "id0", topic: packets.Topic{Name: "name0", SubOptions: packets.SubOptions{Qos: packets.Qos0}}},
|
|
||||||
{clientID: "id1", topic: packets.Topic{Name: "name1", SubOptions: packets.SubOptions{Qos: packets.Qos1}}},
|
|
||||||
}
|
|
||||||
expectedCurrent -= 2
|
|
||||||
for _, v := range utt {
|
|
||||||
a.NoError(store.Unsubscribe(v.clientID, v.topic.Name))
|
|
||||||
}
|
|
||||||
stats = store.GetStats()
|
|
||||||
a.EqualValues(expectedTotal, stats.SubscriptionsTotal)
|
|
||||||
a.EqualValues(expectedCurrent, stats.SubscriptionsCurrent)
|
|
||||||
|
|
||||||
//if unsubscribe not exists topic, current statistics should not decrease
|
|
||||||
a.NoError(store.Unsubscribe("id0", "name555"))
|
|
||||||
stats = store.GetStats()
|
|
||||||
a.EqualValues(len(tt), stats.SubscriptionsTotal)
|
|
||||||
a.EqualValues(expectedCurrent, stats.SubscriptionsCurrent)
|
|
||||||
|
|
||||||
a.NoError(store.Unsubscribe("id4", "$share/abc/name4"))
|
|
||||||
|
|
||||||
expectedCurrent -= 1
|
|
||||||
stats = store.GetStats()
|
|
||||||
a.EqualValues(expectedTotal, stats.SubscriptionsTotal)
|
|
||||||
a.EqualValues(expectedCurrent, stats.SubscriptionsCurrent)
|
|
||||||
|
|
||||||
a.NoError(store.UnsubscribeAll("id4"))
|
|
||||||
expectedCurrent -= 3
|
|
||||||
stats = store.GetStats()
|
|
||||||
a.EqualValues(len(tt), stats.SubscriptionsTotal)
|
|
||||||
a.EqualValues(expectedCurrent, stats.SubscriptionsCurrent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGetClientStats(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
var err error
|
|
||||||
tt := []struct {
|
|
||||||
clientID string
|
|
||||||
topic packets.Topic
|
|
||||||
}{
|
|
||||||
{clientID: "id0", topic: packets.Topic{Name: "name0", SubOptions: packets.SubOptions{Qos: packets.Qos0}}},
|
|
||||||
{clientID: "id0", topic: packets.Topic{Name: "name1", SubOptions: packets.SubOptions{Qos: packets.Qos1}}},
|
|
||||||
// test $share and system topic
|
|
||||||
{clientID: "id0", topic: packets.Topic{Name: "$share/abc/name5", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
{clientID: "id0", topic: packets.Topic{Name: "$SYS/a/b/c", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
|
|
||||||
{clientID: "id1", topic: packets.Topic{Name: "name0", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
{clientID: "id1", topic: packets.Topic{Name: "$share/abc/name5", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
{clientID: "id2", topic: packets.Topic{Name: "$SYS/a/b/c", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
{clientID: "id2", topic: packets.Topic{Name: "name5", SubOptions: packets.SubOptions{Qos: packets.Qos2}}},
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
_, err = store.Subscribe(v.clientID, subscription.FromTopic(v.topic, 0))
|
|
||||||
a.NoError(err)
|
|
||||||
}
|
|
||||||
stats, _ := store.GetClientStats("id0")
|
|
||||||
a.EqualValues(4, stats.SubscriptionsTotal)
|
|
||||||
a.EqualValues(4, stats.SubscriptionsCurrent)
|
|
||||||
|
|
||||||
a.NoError(store.UnsubscribeAll("id0"))
|
|
||||||
stats, _ = store.GetClientStats("id0")
|
|
||||||
a.EqualValues(4, stats.SubscriptionsTotal)
|
|
||||||
a.EqualValues(0, stats.SubscriptionsCurrent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSuite(t *testing.T, new func() subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
store := new()
|
|
||||||
a.Nil(store.Init(nil))
|
|
||||||
defer store.Close()
|
|
||||||
for i := 0; i <= 1; i++ {
|
|
||||||
testAddSubscribe(t, store)
|
|
||||||
t.Run("testGetTopic"+strconv.Itoa(i), func(t *testing.T) {
|
|
||||||
testGetTopic(t, store)
|
|
||||||
})
|
|
||||||
t.Run("testTopicMatch"+strconv.Itoa(i), func(t *testing.T) {
|
|
||||||
testTopicMatch(t, store)
|
|
||||||
})
|
|
||||||
t.Run("testIterate"+strconv.Itoa(i), func(t *testing.T) {
|
|
||||||
testIterate(t, store)
|
|
||||||
})
|
|
||||||
t.Run("testUnsubscribe"+strconv.Itoa(i), func(t *testing.T) {
|
|
||||||
testUnsubscribe(t, store)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
store2 := new()
|
|
||||||
a.Nil(store2.Init(nil))
|
|
||||||
defer store2.Close()
|
|
||||||
t.Run("testGetStatus", func(t *testing.T) {
|
|
||||||
testGetStatus(t, store2)
|
|
||||||
})
|
|
||||||
|
|
||||||
store3 := new()
|
|
||||||
a.Nil(store3.Init(nil))
|
|
||||||
defer store3.Close()
|
|
||||||
t.Run("testGetStatus", func(t *testing.T) {
|
|
||||||
testGetClientStats(t, store3)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
func testGetTopic(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
|
|
||||||
rs := subscription.Get(store, topicA.TopicFilter, subscription.TypeAll)
|
|
||||||
a.Equal(topicA, rs["client1"][0])
|
|
||||||
a.Equal(topicA, rs["client2"][0])
|
|
||||||
|
|
||||||
rs = subscription.Get(store, topicA.TopicFilter, subscription.TypeNonShared)
|
|
||||||
a.Equal(topicA, rs["client1"][0])
|
|
||||||
a.Equal(topicA, rs["client2"][0])
|
|
||||||
|
|
||||||
rs = subscription.Get(store, systemTopicA.TopicFilter, subscription.TypeAll)
|
|
||||||
a.Equal(systemTopicA, rs["client1"][0])
|
|
||||||
a.Equal(systemTopicA, rs["client2"][0])
|
|
||||||
|
|
||||||
rs = subscription.Get(store, systemTopicA.TopicFilter, subscription.TypeSYS)
|
|
||||||
a.Equal(systemTopicA, rs["client1"][0])
|
|
||||||
a.Equal(systemTopicA, rs["client2"][0])
|
|
||||||
|
|
||||||
rs = subscription.Get(store, "$share/"+sharedTopicA1.ShareName+"/"+sharedTopicA1.TopicFilter, subscription.TypeAll)
|
|
||||||
a.Equal(sharedTopicA1, rs["client1"][0])
|
|
||||||
a.Equal(sharedTopicA1, rs["client2"][0])
|
|
||||||
|
|
||||||
}
|
|
||||||
func testTopicMatch(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
rs := subscription.GetTopicMatched(store, topicA.TopicFilter, subscription.TypeAll)
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA, sharedTopicA1, sharedTopicA2}, rs["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA, sharedTopicA1, sharedTopicA2}, rs["client2"])
|
|
||||||
|
|
||||||
rs = subscription.GetTopicMatched(store, topicA.TopicFilter, subscription.TypeNonShared)
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, rs["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, rs["client2"])
|
|
||||||
|
|
||||||
rs = subscription.GetTopicMatched(store, topicA.TopicFilter, subscription.TypeShared)
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1, sharedTopicA2}, rs["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1, sharedTopicA2}, rs["client2"])
|
|
||||||
|
|
||||||
rs = subscription.GetTopicMatched(store, systemTopicA.TopicFilter, subscription.TypeSYS)
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA}, rs["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA}, rs["client2"])
|
|
||||||
|
|
||||||
}
|
|
||||||
func testUnsubscribe(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
a.Nil(store.Unsubscribe("client1", topicA.TopicFilter))
|
|
||||||
rs := subscription.Get(store, topicA.TopicFilter, subscription.TypeAll)
|
|
||||||
a.Nil(rs["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, rs["client2"])
|
|
||||||
a.Nil(store.UnsubscribeAll("client2"))
|
|
||||||
a.Nil(store.UnsubscribeAll("client1"))
|
|
||||||
var iterationCalled bool
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
iterationCalled = true
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{Type: subscription.TypeAll})
|
|
||||||
a.False(iterationCalled)
|
|
||||||
}
|
|
||||||
func testIterate(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
|
|
||||||
var iterationCalled bool
|
|
||||||
// invalid subscription.IterationOptions
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
iterationCalled = true
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{})
|
|
||||||
a.False(iterationCalled)
|
|
||||||
testIterateNonShared(t, store)
|
|
||||||
testIterateShared(t, store)
|
|
||||||
testIterateSystem(t, store)
|
|
||||||
}
|
|
||||||
func testIterateNonShared(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
// iterate all non-shared subscriptions.
|
|
||||||
got := make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeNonShared,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA, topicB}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA, topicB}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all non-shared subscriptions with ClientID option.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeNonShared,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA, topicB}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
|
|
||||||
// iterate all non-shared subscriptions that matched given topic name.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeNonShared,
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
TopicName: topicA.TopicFilter,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all non-shared subscriptions that matched given topic name and client id
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeNonShared,
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
TopicName: topicA.TopicFilter,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
|
|
||||||
// iterate all non-shared subscriptions that matched given topic filter.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeNonShared,
|
|
||||||
MatchType: subscription.MatchFilter,
|
|
||||||
TopicName: topicA.TopicFilter,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all non-shared subscriptions that matched given topic filter and client id
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeNonShared,
|
|
||||||
MatchType: subscription.MatchFilter,
|
|
||||||
TopicName: topicA.TopicFilter,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{topicA}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
}
|
|
||||||
func testIterateShared(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
// iterate all shared subscriptions.
|
|
||||||
got := make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeShared,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1, sharedTopicA2, sharedTopicB1, sharedTopicB2}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1, sharedTopicA2, sharedTopicB1, sharedTopicB2}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all shared subscriptions with ClientID option.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeShared,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1, sharedTopicA2, sharedTopicB1, sharedTopicB2}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
|
|
||||||
// iterate all shared subscriptions that matched given topic filter.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeShared,
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
TopicName: "$share/" + sharedTopicA1.ShareName + "/" + sharedTopicA1.TopicFilter,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all shared subscriptions that matched given topic filter and client id
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeShared,
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
TopicName: "$share/" + sharedTopicA1.ShareName + "/" + sharedTopicA1.TopicFilter,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
|
|
||||||
// iterate all shared subscriptions that matched given topic name.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeShared,
|
|
||||||
MatchType: subscription.MatchFilter,
|
|
||||||
TopicName: sharedTopicA1.TopicFilter,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1, sharedTopicA2}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1, sharedTopicA2}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all shared subscriptions that matched given topic name and clientID
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeShared,
|
|
||||||
MatchType: subscription.MatchFilter,
|
|
||||||
TopicName: sharedTopicA1.TopicFilter,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{sharedTopicA1, sharedTopicA2}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
|
|
||||||
}
|
|
||||||
func testIterateSystem(t *testing.T, store subscription.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
// iterate all system subscriptions.
|
|
||||||
got := make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeSYS,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA, systemTopicB}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA, systemTopicB}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all system subscriptions with ClientID option.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeSYS,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA, systemTopicB}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
|
|
||||||
// iterate all system subscriptions that matched given topic filter.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeSYS,
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
TopicName: systemTopicA.TopicFilter,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all system subscriptions that matched given topic filter and client id
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeSYS,
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
TopicName: systemTopicA.TopicFilter,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
|
|
||||||
// iterate all system subscriptions that matched given topic name.
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeSYS,
|
|
||||||
MatchType: subscription.MatchFilter,
|
|
||||||
TopicName: systemTopicA.TopicFilter,
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA}, got["client1"])
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA}, got["client2"])
|
|
||||||
|
|
||||||
// iterate all system subscriptions that matched given topic name and clientID
|
|
||||||
got = make(subscription.ClientSubscriptions)
|
|
||||||
store.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
got[clientID] = append(got[clientID], sub)
|
|
||||||
return true
|
|
||||||
}, subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeSYS,
|
|
||||||
MatchType: subscription.MatchFilter,
|
|
||||||
TopicName: systemTopicA.TopicFilter,
|
|
||||||
ClientID: "client1",
|
|
||||||
})
|
|
||||||
a.ElementsMatch([]*gmqtt.Subscription{systemTopicA}, got["client1"])
|
|
||||||
a.Len(got["client2"], 0)
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package mem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/unack"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ unack.Store = (*Store)(nil)
|
|
||||||
|
|
||||||
type Store struct {
|
|
||||||
clientID string
|
|
||||||
unackpublish map[packets.PacketID]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
ClientID string
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(opts Options) *Store {
|
|
||||||
return &Store{
|
|
||||||
clientID: opts.ClientID,
|
|
||||||
unackpublish: make(map[packets.PacketID]struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Init(cleanStart bool) error {
|
|
||||||
if cleanStart {
|
|
||||||
s.unackpublish = make(map[packets.PacketID]struct{})
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Set(id packets.PacketID) (bool, error) {
|
|
||||||
if _, ok := s.unackpublish[id]; ok {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
s.unackpublish[id] = struct{}{}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Remove(id packets.PacketID) error {
|
|
||||||
delete(s.unackpublish, id)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gomodule/redigo/redis"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/unack"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
unackPrefix = "unack:"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ unack.Store = (*Store)(nil)
|
|
||||||
|
|
||||||
type Store struct {
|
|
||||||
clientID string
|
|
||||||
pool *redis.Pool
|
|
||||||
unackpublish map[packets.PacketID]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
ClientID string
|
|
||||||
Pool *redis.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(opts Options) *Store {
|
|
||||||
return &Store{
|
|
||||||
clientID: opts.ClientID,
|
|
||||||
pool: opts.Pool,
|
|
||||||
unackpublish: make(map[packets.PacketID]struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getKey(clientID string) string {
|
|
||||||
return unackPrefix + clientID
|
|
||||||
}
|
|
||||||
func (s *Store) Init(cleanStart bool) error {
|
|
||||||
if cleanStart {
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
s.unackpublish = make(map[packets.PacketID]struct{})
|
|
||||||
_, err := c.Do("del", getKey(s.clientID))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Set(id packets.PacketID) (bool, error) {
|
|
||||||
// from cache
|
|
||||||
if _, ok := s.unackpublish[id]; ok {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
_, err := c.Do("hset", getKey(s.clientID), id, 1)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
s.unackpublish[id] = struct{}{}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Remove(id packets.PacketID) error {
|
|
||||||
c := s.pool.Get()
|
|
||||||
defer c.Close()
|
|
||||||
_, err := c.Do("hdel", getKey(s.clientID), id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
delete(s.unackpublish, id)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/unack"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
TestServerConfig = config.Config{}
|
|
||||||
cid = "cid"
|
|
||||||
TestClientID = cid
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSuite(t *testing.T, store unack.Store) {
|
|
||||||
a := assert.New(t)
|
|
||||||
a.Nil(store.Init(false))
|
|
||||||
for i := packets.PacketID(1); i < 10; i++ {
|
|
||||||
rs, err := store.Set(i)
|
|
||||||
a.Nil(err)
|
|
||||||
a.False(rs)
|
|
||||||
rs, err = store.Set(i)
|
|
||||||
a.Nil(err)
|
|
||||||
a.True(rs)
|
|
||||||
err = store.Remove(i)
|
|
||||||
a.Nil(err)
|
|
||||||
rs, err = store.Set(i)
|
|
||||||
a.Nil(err)
|
|
||||||
a.False(rs)
|
|
||||||
|
|
||||||
}
|
|
||||||
a.Nil(store.Init(false))
|
|
||||||
for i := packets.PacketID(1); i < 10; i++ {
|
|
||||||
rs, err := store.Set(i)
|
|
||||||
a.Nil(err)
|
|
||||||
a.True(rs)
|
|
||||||
err = store.Remove(i)
|
|
||||||
a.Nil(err)
|
|
||||||
rs, err = store.Set(i)
|
|
||||||
a.Nil(err)
|
|
||||||
a.False(rs)
|
|
||||||
}
|
|
||||||
a.Nil(store.Init(true))
|
|
||||||
for i := packets.PacketID(1); i < 10; i++ {
|
|
||||||
rs, err := store.Set(i)
|
|
||||||
a.Nil(err)
|
|
||||||
a.False(rs)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package unack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Store represents a unack store for one client.
|
|
||||||
// Unack store is used to persist the unacknowledged qos2 messages.
|
|
||||||
type Store interface {
|
|
||||||
// Init will be called when the client connect.
|
|
||||||
// If cleanStart set to true, the implementation should remove any associated data in backend store.
|
|
||||||
// If it set to false, the implementation should retrieve the associated data from backend store.
|
|
||||||
Init(cleanStart bool) error
|
|
||||||
// Set sets the given id into store.
|
|
||||||
// The return boolean indicates whether the id exist.
|
|
||||||
Set(id packets.PacketID) (bool, error)
|
|
||||||
// Remove removes the given id from store.
|
|
||||||
Remove(id packets.PacketID) error
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: persistence/unack/unack.go
|
|
||||||
|
|
||||||
// Package unack is a generated GoMock package.
|
|
||||||
package unack
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
packets "github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockStore is a mock of Store interface
|
|
||||||
type MockStore struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockStoreMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockStoreMockRecorder is the mock recorder for MockStore
|
|
||||||
type MockStoreMockRecorder struct {
|
|
||||||
mock *MockStore
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockStore creates a new mock instance
|
|
||||||
func NewMockStore(ctrl *gomock.Controller) *MockStore {
|
|
||||||
mock := &MockStore{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockStoreMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *MockStore) EXPECT() *MockStoreMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init mocks base method
|
|
||||||
func (m *MockStore) Init(cleanStart bool) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Init", cleanStart)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init indicates an expected call of Init
|
|
||||||
func (mr *MockStoreMockRecorder) Init(cleanStart interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockStore)(nil).Init), cleanStart)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set mocks base method
|
|
||||||
func (m *MockStore) Set(id packets.PacketID) (bool, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Set", id)
|
|
||||||
ret0, _ := ret[0].(bool)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set indicates an expected call of Set
|
|
||||||
func (mr *MockStoreMockRecorder) Set(id interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockStore)(nil).Set), id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove mocks base method
|
|
||||||
func (m *MockStore) Remove(id packets.PacketID) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Remove", id)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove indicates an expected call of Remove
|
|
||||||
func (mr *MockStoreMockRecorder) Remove(id interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockStore)(nil).Remove), id)
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
# Plugin
|
|
||||||
|
|
||||||
[Gmqtt插件机制详解](https://juejin.cn/post/6908305981923409934)
|
|
||||||
|
|
||||||
## How to write plugins
|
|
||||||
|
|
||||||
Gmqtt uses code generator to generate plugin template.
|
|
||||||
|
|
||||||
First, install the CLI tool:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# run under gmqtt project root directory.
|
|
||||||
go install ./cmd/gmqctl
|
|
||||||
```
|
|
||||||
|
|
||||||
Enjoy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gmqctl gen plugin --help
|
|
||||||
code generator
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
gmqctl gen plugin [flags]
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
The following command will generate a code template for the 'awesome' plugin, which makes use of OnBasicAuth and OnSubscribe hook and enables the configuration in ./plugin directory.
|
|
||||||
|
|
||||||
gmqctl gen plugin -n awesome -H OnBasicAuth,OnSubscribe -c true -o ./plugin
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
-c, --config Whether the plugin needs a configuration.
|
|
||||||
-h, --help help for plugin
|
|
||||||
-H, --hooks string The hooks use by the plugin, multiple hooks are separated by ','
|
|
||||||
-n, --name string The plugin name.
|
|
||||||
-o, --output string The output directory.
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Details...TODO
|
|
@ -1,83 +0,0 @@
|
|||||||
# admin
|
|
||||||
|
|
||||||
Admin plugin use [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) to provide both REST HTTP and GRPC APIs
|
|
||||||
for integration with external systems.
|
|
||||||
|
|
||||||
# API Doc
|
|
||||||
|
|
||||||
See [swagger](https://github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/blob/master/plugin/admin/swagger)
|
|
||||||
|
|
||||||
# Examples
|
|
||||||
|
|
||||||
## List Clients
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl 127.0.0.1:57091/v1/clients
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"clients": [
|
|
||||||
{
|
|
||||||
"client_id": "ab",
|
|
||||||
"username": "",
|
|
||||||
"keep_alive": 60,
|
|
||||||
"version": 4,
|
|
||||||
"remote_addr": "127.0.0.1:51637",
|
|
||||||
"local_addr": "127.0.0.1:58090",
|
|
||||||
"connected_at": "2020-12-12T12:26:36Z",
|
|
||||||
"disconnected_at": null,
|
|
||||||
"session_expiry": 7200,
|
|
||||||
"max_inflight": 100,
|
|
||||||
"inflight_len": 0,
|
|
||||||
"max_queue": 100,
|
|
||||||
"queue_len": 0,
|
|
||||||
"subscriptions_current": 0,
|
|
||||||
"subscriptions_total": 0,
|
|
||||||
"packets_received_bytes": "54",
|
|
||||||
"packets_received_nums": "3",
|
|
||||||
"packets_send_bytes": "8",
|
|
||||||
"packets_send_nums": "2",
|
|
||||||
"message_dropped": "0"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total_count": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Filter Subscriptions
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl 127.0.0.1:57091/v1/filter_subscriptions?filter_type=1,2,3&match_type=1&topic_name=/a
|
|
||||||
```
|
|
||||||
|
|
||||||
This curl is able to filter the subscription that the topic name is equal to "/a".
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"subscriptions": [
|
|
||||||
{
|
|
||||||
"topic_name": "/a",
|
|
||||||
"id": 0,
|
|
||||||
"qos": 1,
|
|
||||||
"no_local": false,
|
|
||||||
"retain_as_published": false,
|
|
||||||
"retain_handling": 0,
|
|
||||||
"client_id": "ab"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Publish Message
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl -X POST 127.0.0.1:57091/v1/publish -d '{"topic_name":"a","payload":"test","qos":1}'
|
|
||||||
```
|
|
||||||
|
|
||||||
This curl will publish the message to the broker.The broker will check if there are matched topics and send the message
|
|
||||||
to the subscribers, just like received a message from a MQTT client.
|
|
@ -1,72 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ server.Plugin = (*Admin)(nil)
|
|
||||||
|
|
||||||
const Name = "admin"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
server.RegisterPlugin(Name, New)
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(config config.Config) (server.Plugin, error) {
|
|
||||||
return &Admin{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var log *zap.Logger
|
|
||||||
|
|
||||||
// Admin providers gRPC and HTTP API that enables the external system to interact with the broker.
|
|
||||||
type Admin struct {
|
|
||||||
statsReader server.StatsReader
|
|
||||||
publisher server.Publisher
|
|
||||||
clientService server.ClientService
|
|
||||||
store *store
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) registerHTTP(g server.APIRegistrar) (err error) {
|
|
||||||
err = g.RegisterHTTPHandler(RegisterClientServiceHandlerFromEndpoint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = g.RegisterHTTPHandler(RegisterSubscriptionServiceHandlerFromEndpoint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = g.RegisterHTTPHandler(RegisterPublishServiceHandlerFromEndpoint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) Load(service server.Server) error {
|
|
||||||
log = server.LoggerWithField(zap.String("plugin", Name))
|
|
||||||
apiRegistrar := service.APIRegistrar()
|
|
||||||
RegisterClientServiceServer(apiRegistrar, &clientService{a: a})
|
|
||||||
RegisterSubscriptionServiceServer(apiRegistrar, &subscriptionService{a: a})
|
|
||||||
RegisterPublishServiceServer(apiRegistrar, &publisher{a: a})
|
|
||||||
err := a.registerHTTP(apiRegistrar)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.statsReader = service.StatsManager()
|
|
||||||
a.store = newStore(a.statsReader, service.GetConfig())
|
|
||||||
a.store.subscriptionService = service.SubscriptionService()
|
|
||||||
a.publisher = service.Publisher()
|
|
||||||
a.clientService = service.ClientService()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) Unload() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) Name() string {
|
|
||||||
return Name
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientService struct {
|
|
||||||
a *Admin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientService) mustEmbedUnimplementedClientServiceServer() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// List lists alertclient information which the session is valid in the broker (both connected and disconnected).
|
|
||||||
func (c *clientService) List(ctx context.Context, req *ListClientRequest) (*ListClientResponse, error) {
|
|
||||||
page, pageSize := GetPage(req.Page, req.PageSize)
|
|
||||||
clients, total, err := c.a.store.GetClients(page, pageSize)
|
|
||||||
if err != nil {
|
|
||||||
return &ListClientResponse{}, err
|
|
||||||
}
|
|
||||||
return &ListClientResponse{
|
|
||||||
Clients: clients,
|
|
||||||
TotalCount: total,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the client information for given request client id.
|
|
||||||
func (c *clientService) Get(ctx context.Context, req *GetClientRequest) (*GetClientResponse, error) {
|
|
||||||
if req.ClientId == "" {
|
|
||||||
return nil, ErrInvalidArgument("client_id", "")
|
|
||||||
}
|
|
||||||
client := c.a.store.GetClientByID(req.ClientId)
|
|
||||||
if client == nil {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
return &GetClientResponse{
|
|
||||||
Client: client,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete force disconnect.
|
|
||||||
func (c *clientService) Delete(ctx context.Context, req *DeleteClientRequest) (*empty.Empty, error) {
|
|
||||||
if req.ClientId == "" {
|
|
||||||
return nil, ErrInvalidArgument("client_id", "")
|
|
||||||
}
|
|
||||||
if req.CleanSession {
|
|
||||||
c.a.clientService.TerminateSession(req.ClientId)
|
|
||||||
} else {
|
|
||||||
client := c.a.clientService.GetClient(req.ClientId)
|
|
||||||
if client != nil {
|
|
||||||
client.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &empty.Empty{}, nil
|
|
||||||
}
|
|
@ -1,739 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.22.0
|
|
||||||
// protoc v3.13.0
|
|
||||||
// source: client.proto
|
|
||||||
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
empty "github.com/golang/protobuf/ptypes/empty"
|
|
||||||
timestamp "github.com/golang/protobuf/ptypes/timestamp"
|
|
||||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
|
||||||
// of the legacy proto package is being used.
|
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
|
||||||
|
|
||||||
type ListClientRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
PageSize uint32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
|
|
||||||
Page uint32 `protobuf:"varint,2,opt,name=page,proto3" json:"page,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListClientRequest) Reset() {
|
|
||||||
*x = ListClientRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_client_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListClientRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ListClientRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *ListClientRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_client_proto_msgTypes[0]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ListClientRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*ListClientRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_client_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListClientRequest) GetPageSize() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.PageSize
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListClientRequest) GetPage() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Page
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListClientResponse struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Clients []*Client `protobuf:"bytes,1,rep,name=alertclient,proto3" json:"alertclient,omitempty"`
|
|
||||||
TotalCount uint32 `protobuf:"varint,2,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListClientResponse) Reset() {
|
|
||||||
*x = ListClientResponse{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_client_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListClientResponse) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ListClientResponse) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *ListClientResponse) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_client_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ListClientResponse.ProtoReflect.Descriptor instead.
|
|
||||||
func (*ListClientResponse) Descriptor() ([]byte, []int) {
|
|
||||||
return file_client_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListClientResponse) GetClients() []*Client {
|
|
||||||
if x != nil {
|
|
||||||
return x.Clients
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListClientResponse) GetTotalCount() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.TotalCount
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetClientRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *GetClientRequest) Reset() {
|
|
||||||
*x = GetClientRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_client_proto_msgTypes[2]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *GetClientRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*GetClientRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *GetClientRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_client_proto_msgTypes[2]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use GetClientRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*GetClientRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_client_proto_rawDescGZIP(), []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *GetClientRequest) GetClientId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ClientId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetClientResponse struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *GetClientResponse) Reset() {
|
|
||||||
*x = GetClientResponse{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_client_proto_msgTypes[3]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *GetClientResponse) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*GetClientResponse) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *GetClientResponse) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_client_proto_msgTypes[3]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use GetClientResponse.ProtoReflect.Descriptor instead.
|
|
||||||
func (*GetClientResponse) Descriptor() ([]byte, []int) {
|
|
||||||
return file_client_proto_rawDescGZIP(), []int{3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *GetClientResponse) GetClient() *Client {
|
|
||||||
if x != nil {
|
|
||||||
return x.Client
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteClientRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
|
||||||
CleanSession bool `protobuf:"varint,2,opt,name=clean_session,json=cleanSession,proto3" json:"clean_session,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *DeleteClientRequest) Reset() {
|
|
||||||
*x = DeleteClientRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_client_proto_msgTypes[4]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *DeleteClientRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*DeleteClientRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *DeleteClientRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_client_proto_msgTypes[4]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use DeleteClientRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*DeleteClientRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_client_proto_rawDescGZIP(), []int{4}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *DeleteClientRequest) GetClientId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ClientId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *DeleteClientRequest) GetCleanSession() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.CleanSession
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
|
||||||
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
|
|
||||||
KeepAlive int32 `protobuf:"varint,3,opt,name=keep_alive,json=keepAlive,proto3" json:"keep_alive,omitempty"`
|
|
||||||
Version int32 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
|
|
||||||
RemoteAddr string `protobuf:"bytes,5,opt,name=remote_addr,json=remoteAddr,proto3" json:"remote_addr,omitempty"`
|
|
||||||
LocalAddr string `protobuf:"bytes,6,opt,name=local_addr,json=localAddr,proto3" json:"local_addr,omitempty"`
|
|
||||||
ConnectedAt *timestamp.Timestamp `protobuf:"bytes,7,opt,name=connected_at,json=connectedAt,proto3" json:"connected_at,omitempty"`
|
|
||||||
DisconnectedAt *timestamp.Timestamp `protobuf:"bytes,8,opt,name=disconnected_at,json=disconnectedAt,proto3" json:"disconnected_at,omitempty"`
|
|
||||||
SessionExpiry uint32 `protobuf:"varint,9,opt,name=session_expiry,json=sessionExpiry,proto3" json:"session_expiry,omitempty"`
|
|
||||||
MaxInflight uint32 `protobuf:"varint,10,opt,name=max_inflight,json=maxInflight,proto3" json:"max_inflight,omitempty"`
|
|
||||||
InflightLen uint32 `protobuf:"varint,11,opt,name=inflight_len,json=inflightLen,proto3" json:"inflight_len,omitempty"`
|
|
||||||
MaxQueue uint32 `protobuf:"varint,12,opt,name=max_queue,json=maxQueue,proto3" json:"max_queue,omitempty"`
|
|
||||||
QueueLen uint32 `protobuf:"varint,13,opt,name=queue_len,json=queueLen,proto3" json:"queue_len,omitempty"`
|
|
||||||
SubscriptionsCurrent uint32 `protobuf:"varint,14,opt,name=subscriptions_current,json=subscriptionsCurrent,proto3" json:"subscriptions_current,omitempty"`
|
|
||||||
SubscriptionsTotal uint32 `protobuf:"varint,15,opt,name=subscriptions_total,json=subscriptionsTotal,proto3" json:"subscriptions_total,omitempty"`
|
|
||||||
PacketsReceivedBytes uint64 `protobuf:"varint,16,opt,name=packets_received_bytes,json=packetsReceivedBytes,proto3" json:"packets_received_bytes,omitempty"`
|
|
||||||
PacketsReceivedNums uint64 `protobuf:"varint,17,opt,name=packets_received_nums,json=packetsReceivedNums,proto3" json:"packets_received_nums,omitempty"`
|
|
||||||
PacketsSendBytes uint64 `protobuf:"varint,18,opt,name=packets_send_bytes,json=packetsSendBytes,proto3" json:"packets_send_bytes,omitempty"`
|
|
||||||
PacketsSendNums uint64 `protobuf:"varint,19,opt,name=packets_send_nums,json=packetsSendNums,proto3" json:"packets_send_nums,omitempty"`
|
|
||||||
MessageDropped uint64 `protobuf:"varint,20,opt,name=message_dropped,json=messageDropped,proto3" json:"message_dropped,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) Reset() {
|
|
||||||
*x = Client{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_client_proto_msgTypes[5]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Client) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Client) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_client_proto_msgTypes[5]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Client.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Client) Descriptor() ([]byte, []int) {
|
|
||||||
return file_client_proto_rawDescGZIP(), []int{5}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetClientId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ClientId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetUsername() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Username
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetKeepAlive() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.KeepAlive
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetVersion() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Version
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetRemoteAddr() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.RemoteAddr
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetLocalAddr() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.LocalAddr
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetConnectedAt() *timestamp.Timestamp {
|
|
||||||
if x != nil {
|
|
||||||
return x.ConnectedAt
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetDisconnectedAt() *timestamp.Timestamp {
|
|
||||||
if x != nil {
|
|
||||||
return x.DisconnectedAt
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetSessionExpiry() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.SessionExpiry
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetMaxInflight() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.MaxInflight
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetInflightLen() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.InflightLen
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetMaxQueue() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.MaxQueue
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetQueueLen() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.QueueLen
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetSubscriptionsCurrent() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.SubscriptionsCurrent
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetSubscriptionsTotal() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.SubscriptionsTotal
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetPacketsReceivedBytes() uint64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.PacketsReceivedBytes
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetPacketsReceivedNums() uint64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.PacketsReceivedNums
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetPacketsSendBytes() uint64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.PacketsSendBytes
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetPacketsSendNums() uint64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.PacketsSendNums
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Client) GetMessageDropped() uint64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.MessageDropped
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_client_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_client_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f,
|
|
||||||
0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x1a,
|
|
||||||
0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f,
|
|
||||||
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67,
|
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65,
|
|
||||||
0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67,
|
|
||||||
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65,
|
|
||||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x44, 0x0a, 0x11, 0x4c,
|
|
||||||
0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
|
||||||
0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20,
|
|
||||||
0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a,
|
|
||||||
0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67,
|
|
||||||
0x65, 0x22, 0x68, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52,
|
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
|
||||||
0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74,
|
|
||||||
0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e,
|
|
||||||
0x74, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f,
|
|
||||||
0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
|
||||||
0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2f, 0x0a, 0x10, 0x47,
|
|
||||||
0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
|
||||||
0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x44, 0x0a, 0x11,
|
|
||||||
0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
|
||||||
0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
|
||||||
0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e,
|
|
||||||
0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65,
|
|
||||||
0x6e, 0x74, 0x22, 0x57, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65,
|
|
||||||
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69,
|
|
||||||
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c,
|
|
||||||
0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x5f,
|
|
||||||
0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x63,
|
|
||||||
0x6c, 0x65, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xb8, 0x06, 0x0a, 0x06,
|
|
||||||
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
|
||||||
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
|
||||||
0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
|
||||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
|
||||||
0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20,
|
|
||||||
0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x18,
|
|
||||||
0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52,
|
|
||||||
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f,
|
|
||||||
0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72,
|
|
||||||
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63,
|
|
||||||
0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c,
|
|
||||||
0x6f, 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e,
|
|
||||||
0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
|
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
|
||||||
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e,
|
|
||||||
0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x43, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x63, 0x6f,
|
|
||||||
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b,
|
|
||||||
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
|
||||||
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x64, 0x69,
|
|
||||||
0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x25, 0x0a, 0x0e,
|
|
||||||
0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x09,
|
|
||||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x70,
|
|
||||||
0x69, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x66, 0x6c, 0x69,
|
|
||||||
0x67, 0x68, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x49, 0x6e,
|
|
||||||
0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x66, 0x6c, 0x69, 0x67,
|
|
||||||
0x68, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x69, 0x6e,
|
|
||||||
0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78,
|
|
||||||
0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x61,
|
|
||||||
0x78, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f,
|
|
||||||
0x6c, 0x65, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x65, 0x75, 0x65,
|
|
||||||
0x4c, 0x65, 0x6e, 0x12, 0x33, 0x0a, 0x15, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
|
||||||
0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01,
|
|
||||||
0x28, 0x0d, 0x52, 0x14, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
|
||||||
0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x73, 0x75, 0x62, 0x73,
|
|
||||||
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18,
|
|
||||||
0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
|
||||||
0x69, 0x6f, 0x6e, 0x73, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x61, 0x63,
|
|
||||||
0x6b, 0x65, 0x74, 0x73, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x79,
|
|
||||||
0x74, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x70, 0x61, 0x63, 0x6b, 0x65,
|
|
||||||
0x74, 0x73, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12,
|
|
||||||
0x32, 0x0a, 0x15, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69,
|
|
||||||
0x76, 0x65, 0x64, 0x5f, 0x6e, 0x75, 0x6d, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13,
|
|
||||||
0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x4e,
|
|
||||||
0x75, 0x6d, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x5f, 0x73,
|
|
||||||
0x65, 0x6e, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x04, 0x52,
|
|
||||||
0x10, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x53, 0x65, 0x6e, 0x64, 0x42, 0x79, 0x74, 0x65,
|
|
||||||
0x73, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x5f, 0x73, 0x65, 0x6e,
|
|
||||||
0x64, 0x5f, 0x6e, 0x75, 0x6d, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x61,
|
|
||||||
0x63, 0x6b, 0x65, 0x74, 0x73, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x75, 0x6d, 0x73, 0x12, 0x27, 0x0a,
|
|
||||||
0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64,
|
|
||||||
0x18, 0x14, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44,
|
|
||||||
0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x32, 0xcd, 0x02, 0x0a, 0x0d, 0x43, 0x6c, 0x69, 0x65, 0x6e,
|
|
||||||
0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x64, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74,
|
|
||||||
0x12, 0x22, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61,
|
|
||||||
0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
|
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d,
|
|
||||||
0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e,
|
|
||||||
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02,
|
|
||||||
0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x6d,
|
|
||||||
0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x21, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64,
|
|
||||||
0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e,
|
|
||||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74,
|
|
||||||
0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c,
|
|
||||||
0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3,
|
|
||||||
0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
|
||||||
0x73, 0x2f, 0x7b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x67, 0x0a,
|
|
||||||
0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e,
|
|
||||||
0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
|
||||||
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
|
|
||||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
|
||||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f,
|
|
||||||
0x76, 0x31, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x63, 0x6c, 0x69, 0x65,
|
|
||||||
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x61, 0x64, 0x6d, 0x69,
|
|
||||||
0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_client_proto_rawDescOnce sync.Once
|
|
||||||
file_client_proto_rawDescData = file_client_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_client_proto_rawDescGZIP() []byte {
|
|
||||||
file_client_proto_rawDescOnce.Do(func() {
|
|
||||||
file_client_proto_rawDescData = protoimpl.X.CompressGZIP(file_client_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_client_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
|
||||||
var file_client_proto_goTypes = []interface{}{
|
|
||||||
(*ListClientRequest)(nil), // 0: gmqtt.admin.api.ListClientRequest
|
|
||||||
(*ListClientResponse)(nil), // 1: gmqtt.admin.api.ListClientResponse
|
|
||||||
(*GetClientRequest)(nil), // 2: gmqtt.admin.api.GetClientRequest
|
|
||||||
(*GetClientResponse)(nil), // 3: gmqtt.admin.api.GetClientResponse
|
|
||||||
(*DeleteClientRequest)(nil), // 4: gmqtt.admin.api.DeleteClientRequest
|
|
||||||
(*Client)(nil), // 5: gmqtt.admin.api.Client
|
|
||||||
(*timestamp.Timestamp)(nil), // 6: google.protobuf.Timestamp
|
|
||||||
(*empty.Empty)(nil), // 7: google.protobuf.Empty
|
|
||||||
}
|
|
||||||
var file_client_proto_depIdxs = []int32{
|
|
||||||
5, // 0: gmqtt.admin.api.ListClientResponse.alertclient:type_name -> gmqtt.admin.api.Client
|
|
||||||
5, // 1: gmqtt.admin.api.GetClientResponse.client:type_name -> gmqtt.admin.api.Client
|
|
||||||
6, // 2: gmqtt.admin.api.Client.connected_at:type_name -> google.protobuf.Timestamp
|
|
||||||
6, // 3: gmqtt.admin.api.Client.disconnected_at:type_name -> google.protobuf.Timestamp
|
|
||||||
0, // 4: gmqtt.admin.api.ClientService.List:input_type -> gmqtt.admin.api.ListClientRequest
|
|
||||||
2, // 5: gmqtt.admin.api.ClientService.Get:input_type -> gmqtt.admin.api.GetClientRequest
|
|
||||||
4, // 6: gmqtt.admin.api.ClientService.Delete:input_type -> gmqtt.admin.api.DeleteClientRequest
|
|
||||||
1, // 7: gmqtt.admin.api.ClientService.List:output_type -> gmqtt.admin.api.ListClientResponse
|
|
||||||
3, // 8: gmqtt.admin.api.ClientService.Get:output_type -> gmqtt.admin.api.GetClientResponse
|
|
||||||
7, // 9: gmqtt.admin.api.ClientService.Delete:output_type -> google.protobuf.Empty
|
|
||||||
7, // [7:10] is the sub-list for method output_type
|
|
||||||
4, // [4:7] is the sub-list for method input_type
|
|
||||||
4, // [4:4] is the sub-list for extension type_name
|
|
||||||
4, // [4:4] is the sub-list for extension extendee
|
|
||||||
0, // [0:4] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_client_proto_init() }
|
|
||||||
func file_client_proto_init() {
|
|
||||||
if File_client_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_client_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*ListClientRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_client_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*ListClientResponse); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_client_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*GetClientRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_client_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*GetClientResponse); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_client_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*DeleteClientRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_client_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Client); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_client_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 6,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 1,
|
|
||||||
},
|
|
||||||
GoTypes: file_client_proto_goTypes,
|
|
||||||
DependencyIndexes: file_client_proto_depIdxs,
|
|
||||||
MessageInfos: file_client_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_client_proto = out.File
|
|
||||||
file_client_proto_rawDesc = nil
|
|
||||||
file_client_proto_goTypes = nil
|
|
||||||
file_client_proto_depIdxs = nil
|
|
||||||
}
|
|
@ -1,373 +0,0 @@
|
|||||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
|
||||||
// source: client.proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package admin is a reverse proxy.
|
|
||||||
|
|
||||||
It translates gRPC into RESTful JSON APIs.
|
|
||||||
*/
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/descriptor"
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Suppress "imported and not used" errors
|
|
||||||
var _ codes.Code
|
|
||||||
var _ io.Reader
|
|
||||||
var _ status.Status
|
|
||||||
var _ = runtime.String
|
|
||||||
var _ = utilities.NewDoubleArray
|
|
||||||
var _ = descriptor.ForMessage
|
|
||||||
|
|
||||||
var (
|
|
||||||
filter_ClientService_List_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
|
||||||
)
|
|
||||||
|
|
||||||
func request_ClientService_List_0(ctx context.Context, marshaler runtime.Marshaler, client ClientServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq ListClientRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
if err := req.ParseForm(); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ClientService_List_0); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.List(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_ClientService_List_0(ctx context.Context, marshaler runtime.Marshaler, server ClientServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq ListClientRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ClientService_List_0); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.List(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func request_ClientService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client ClientServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq GetClientRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
var (
|
|
||||||
val string
|
|
||||||
ok bool
|
|
||||||
err error
|
|
||||||
_ = err
|
|
||||||
)
|
|
||||||
|
|
||||||
val, ok = pathParams["client_id"]
|
|
||||||
if !ok {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id")
|
|
||||||
}
|
|
||||||
|
|
||||||
protoReq.ClientId, err = runtime.String(val)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_ClientService_Get_0(ctx context.Context, marshaler runtime.Marshaler, server ClientServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq GetClientRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
var (
|
|
||||||
val string
|
|
||||||
ok bool
|
|
||||||
err error
|
|
||||||
_ = err
|
|
||||||
)
|
|
||||||
|
|
||||||
val, ok = pathParams["client_id"]
|
|
||||||
if !ok {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id")
|
|
||||||
}
|
|
||||||
|
|
||||||
protoReq.ClientId, err = runtime.String(val)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.Get(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
filter_ClientService_Delete_0 = &utilities.DoubleArray{Encoding: map[string]int{"client_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
|
|
||||||
)
|
|
||||||
|
|
||||||
func request_ClientService_Delete_0(ctx context.Context, marshaler runtime.Marshaler, client ClientServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq DeleteClientRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
var (
|
|
||||||
val string
|
|
||||||
ok bool
|
|
||||||
err error
|
|
||||||
_ = err
|
|
||||||
)
|
|
||||||
|
|
||||||
val, ok = pathParams["client_id"]
|
|
||||||
if !ok {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id")
|
|
||||||
}
|
|
||||||
|
|
||||||
protoReq.ClientId, err = runtime.String(val)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := req.ParseForm(); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ClientService_Delete_0); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.Delete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_ClientService_Delete_0(ctx context.Context, marshaler runtime.Marshaler, server ClientServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq DeleteClientRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
var (
|
|
||||||
val string
|
|
||||||
ok bool
|
|
||||||
err error
|
|
||||||
_ = err
|
|
||||||
)
|
|
||||||
|
|
||||||
val, ok = pathParams["client_id"]
|
|
||||||
if !ok {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id")
|
|
||||||
}
|
|
||||||
|
|
||||||
protoReq.ClientId, err = runtime.String(val)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ClientService_Delete_0); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.Delete(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterClientServiceHandlerServer registers the http handlers for service ClientService to "mux".
|
|
||||||
// UnaryRPC :call ClientServiceServer directly.
|
|
||||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
|
||||||
func RegisterClientServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ClientServiceServer) error {
|
|
||||||
|
|
||||||
mux.Handle("GET", pattern_ClientService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_ClientService_List_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_ClientService_List_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("GET", pattern_ClientService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_ClientService_Get_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_ClientService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("DELETE", pattern_ClientService_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_ClientService_Delete_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_ClientService_Delete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterClientServiceHandlerFromEndpoint is same as RegisterClientServiceHandler but
|
|
||||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
|
||||||
func RegisterClientServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
|
||||||
conn, err := grpc.Dial(endpoint, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
if cerr := conn.Close(); cerr != nil {
|
|
||||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
if cerr := conn.Close(); cerr != nil {
|
|
||||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return RegisterClientServiceHandler(ctx, mux, conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterClientServiceHandler registers the http handlers for service ClientService to "mux".
|
|
||||||
// The handlers forward requests to the grpc endpoint over "conn".
|
|
||||||
func RegisterClientServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
|
||||||
return RegisterClientServiceHandlerClient(ctx, mux, NewClientServiceClient(conn))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterClientServiceHandlerClient registers the http handlers for service ClientService
|
|
||||||
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ClientServiceClient".
|
|
||||||
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ClientServiceClient"
|
|
||||||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
|
||||||
// "ClientServiceClient" to call the correct interceptors.
|
|
||||||
func RegisterClientServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ClientServiceClient) error {
|
|
||||||
|
|
||||||
mux.Handle("GET", pattern_ClientService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_ClientService_List_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_ClientService_List_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("GET", pattern_ClientService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_ClientService_Get_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_ClientService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("DELETE", pattern_ClientService_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_ClientService_Delete_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_ClientService_Delete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
pattern_ClientService_List_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "alertclient"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
|
|
||||||
pattern_ClientService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "alertclient", "client_id"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
|
|
||||||
pattern_ClientService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "alertclient", "client_id"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
forward_ClientService_List_0 = runtime.ForwardResponseMessage
|
|
||||||
|
|
||||||
forward_ClientService_Get_0 = runtime.ForwardResponseMessage
|
|
||||||
|
|
||||||
forward_ClientService_Delete_0 = runtime.ForwardResponseMessage
|
|
||||||
)
|
|
@ -1,179 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
|
||||||
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "context"
|
|
||||||
|
|
||||||
empty "github.com/golang/protobuf/ptypes/empty"
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
codes "google.golang.org/grpc/codes"
|
|
||||||
status "google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the grpc package it is being compiled against.
|
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
|
||||||
|
|
||||||
// ClientServiceClient is the client API for ClientService service.
|
|
||||||
//
|
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
|
||||||
type ClientServiceClient interface {
|
|
||||||
// List alertclient
|
|
||||||
List(ctx context.Context, in *ListClientRequest, opts ...grpc.CallOption) (*ListClientResponse, error)
|
|
||||||
// Get the client for given client id.
|
|
||||||
// Return NotFound error when client not found.
|
|
||||||
Get(ctx context.Context, in *GetClientRequest, opts ...grpc.CallOption) (*GetClientResponse, error)
|
|
||||||
// Disconnect the client for given client id.
|
|
||||||
Delete(ctx context.Context, in *DeleteClientRequest, opts ...grpc.CallOption) (*empty.Empty, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type clientServiceClient struct {
|
|
||||||
cc grpc.ClientConnInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClientServiceClient(cc grpc.ClientConnInterface) ClientServiceClient {
|
|
||||||
return &clientServiceClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientServiceClient) List(ctx context.Context, in *ListClientRequest, opts ...grpc.CallOption) (*ListClientResponse, error) {
|
|
||||||
out := new(ListClientResponse)
|
|
||||||
err := c.cc.Invoke(ctx, "/gmqtt.admin.api.ClientService/List", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientServiceClient) Get(ctx context.Context, in *GetClientRequest, opts ...grpc.CallOption) (*GetClientResponse, error) {
|
|
||||||
out := new(GetClientResponse)
|
|
||||||
err := c.cc.Invoke(ctx, "/gmqtt.admin.api.ClientService/Get", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientServiceClient) Delete(ctx context.Context, in *DeleteClientRequest, opts ...grpc.CallOption) (*empty.Empty, error) {
|
|
||||||
out := new(empty.Empty)
|
|
||||||
err := c.cc.Invoke(ctx, "/gmqtt.admin.api.ClientService/Delete", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientServiceServer is the server API for ClientService service.
|
|
||||||
// All implementations must embed UnimplementedClientServiceServer
|
|
||||||
// for forward compatibility
|
|
||||||
type ClientServiceServer interface {
|
|
||||||
// List alertclient
|
|
||||||
List(context.Context, *ListClientRequest) (*ListClientResponse, error)
|
|
||||||
// Get the client for given client id.
|
|
||||||
// Return NotFound error when client not found.
|
|
||||||
Get(context.Context, *GetClientRequest) (*GetClientResponse, error)
|
|
||||||
// Disconnect the client for given client id.
|
|
||||||
Delete(context.Context, *DeleteClientRequest) (*empty.Empty, error)
|
|
||||||
mustEmbedUnimplementedClientServiceServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedClientServiceServer must be embedded to have forward compatible implementations.
|
|
||||||
type UnimplementedClientServiceServer struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (UnimplementedClientServiceServer) List(context.Context, *ListClientRequest) (*ListClientResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method List not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedClientServiceServer) Get(context.Context, *GetClientRequest) (*GetClientResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedClientServiceServer) Delete(context.Context, *DeleteClientRequest) (*empty.Empty, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedClientServiceServer) mustEmbedUnimplementedClientServiceServer() {}
|
|
||||||
|
|
||||||
// UnsafeClientServiceServer may be embedded to opt out of forward compatibility for this service.
|
|
||||||
// Use of this interface is not recommended, as added methods to ClientServiceServer will
|
|
||||||
// result in compilation errors.
|
|
||||||
type UnsafeClientServiceServer interface {
|
|
||||||
mustEmbedUnimplementedClientServiceServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterClientServiceServer(s grpc.ServiceRegistrar, srv ClientServiceServer) {
|
|
||||||
s.RegisterService(&_ClientService_serviceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _ClientService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(ListClientRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(ClientServiceServer).List(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/gmqtt.admin.api.ClientService/List",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(ClientServiceServer).List(ctx, req.(*ListClientRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _ClientService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(GetClientRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(ClientServiceServer).Get(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/gmqtt.admin.api.ClientService/Get",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(ClientServiceServer).Get(ctx, req.(*GetClientRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _ClientService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(DeleteClientRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(ClientServiceServer).Delete(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/gmqtt.admin.api.ClientService/Delete",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(ClientServiceServer).Delete(ctx, req.(*DeleteClientRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ClientService_serviceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "gmqtt.admin.api.ClientService",
|
|
||||||
HandlerType: (*ClientServiceServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "List",
|
|
||||||
Handler: _ClientService_List_Handler,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MethodName: "Get",
|
|
||||||
Handler: _ClientService_Get_Handler,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MethodName: "Delete",
|
|
||||||
Handler: _ClientService_Delete_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{},
|
|
||||||
Metadata: "client.proto",
|
|
||||||
}
|
|
@ -1,179 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
var mockConfig = config.Config{
|
|
||||||
MQTT: config.MQTT{
|
|
||||||
MaxQueuedMsg: 10,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type dummyConn struct {
|
|
||||||
net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalAddr returns the local network address.
|
|
||||||
func (d *dummyConn) LocalAddr() net.Addr {
|
|
||||||
return &net.TCPAddr{}
|
|
||||||
}
|
|
||||||
func (d *dummyConn) RemoteAddr() net.Addr {
|
|
||||||
return &net.TCPAddr{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClientService_List_Get(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
cs := server.NewMockClientService(ctrl)
|
|
||||||
sr := server.NewMockStatsReader(ctrl)
|
|
||||||
|
|
||||||
admin := &Admin{
|
|
||||||
statsReader: sr,
|
|
||||||
clientService: cs,
|
|
||||||
store: newStore(sr, mockConfig),
|
|
||||||
}
|
|
||||||
c := &clientService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
now := time.Now()
|
|
||||||
client := server.NewMockClient(ctrl)
|
|
||||||
client.EXPECT().Version().Return(packets.Version5).AnyTimes()
|
|
||||||
client.EXPECT().Connection().Return(&dummyConn{}).AnyTimes()
|
|
||||||
client.EXPECT().ConnectedAt().Return(now).AnyTimes()
|
|
||||||
created := admin.OnSessionCreatedWrapper(func(ctx context.Context, client server.Client) {})
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
sr.EXPECT().GetClientStats(strconv.Itoa(i)).AnyTimes()
|
|
||||||
client.EXPECT().ClientOptions().Return(&server.ClientOptions{
|
|
||||||
ClientID: strconv.Itoa(i),
|
|
||||||
Username: strconv.Itoa(i),
|
|
||||||
KeepAlive: uint16(i),
|
|
||||||
SessionExpiry: uint32(i),
|
|
||||||
MaxInflight: uint16(i),
|
|
||||||
ReceiveMax: uint16(i),
|
|
||||||
ClientMaxPacketSize: uint32(i),
|
|
||||||
ServerMaxPacketSize: uint32(i),
|
|
||||||
ClientTopicAliasMax: uint16(i),
|
|
||||||
ServerTopicAliasMax: uint16(i),
|
|
||||||
RequestProblemInfo: true,
|
|
||||||
UserProperties: []*packets.UserProperty{
|
|
||||||
{
|
|
||||||
K: []byte{1, 2},
|
|
||||||
V: []byte{1, 2},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RetainAvailable: true,
|
|
||||||
WildcardSubAvailable: true,
|
|
||||||
SubIDAvailable: true,
|
|
||||||
SharedSubAvailable: true,
|
|
||||||
})
|
|
||||||
created(context.Background(), client)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.List(context.Background(), &ListClientRequest{
|
|
||||||
PageSize: 0,
|
|
||||||
Page: 0,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
a.Len(resp.Clients, 10)
|
|
||||||
for k, v := range resp.Clients {
|
|
||||||
addr := net.TCPAddr{}
|
|
||||||
a.Equal(&Client{
|
|
||||||
ClientId: strconv.Itoa(k),
|
|
||||||
Username: strconv.Itoa(k),
|
|
||||||
KeepAlive: int32(k),
|
|
||||||
Version: int32(packets.Version5),
|
|
||||||
RemoteAddr: addr.String(),
|
|
||||||
LocalAddr: addr.String(),
|
|
||||||
ConnectedAt: timestamppb.New(now),
|
|
||||||
DisconnectedAt: nil,
|
|
||||||
SessionExpiry: uint32(k),
|
|
||||||
MaxInflight: uint32(k),
|
|
||||||
MaxQueue: uint32(mockConfig.MQTT.MaxQueuedMsg),
|
|
||||||
PacketsReceivedBytes: 0,
|
|
||||||
PacketsReceivedNums: 0,
|
|
||||||
PacketsSendBytes: 0,
|
|
||||||
PacketsSendNums: 0,
|
|
||||||
MessageDropped: 0,
|
|
||||||
}, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
getResp, err := c.Get(context.Background(), &GetClientRequest{
|
|
||||||
ClientId: "1",
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
a.Equal(resp.Clients[1], getResp.Client)
|
|
||||||
|
|
||||||
pagingResp, err := c.List(context.Background(), &ListClientRequest{
|
|
||||||
PageSize: 2,
|
|
||||||
Page: 2,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
a.Len(pagingResp.Clients, 2)
|
|
||||||
a.Equal(resp.Clients[2], pagingResp.Clients[0])
|
|
||||||
a.Equal(resp.Clients[3], pagingResp.Clients[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClientService_Delete(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
cs := server.NewMockClientService(ctrl)
|
|
||||||
sr := server.NewMockStatsReader(ctrl)
|
|
||||||
|
|
||||||
admin := &Admin{
|
|
||||||
statsReader: sr,
|
|
||||||
clientService: cs,
|
|
||||||
store: newStore(sr, mockConfig),
|
|
||||||
}
|
|
||||||
c := &clientService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
client := server.NewMockClient(ctrl)
|
|
||||||
client.EXPECT().Close()
|
|
||||||
cs.EXPECT().GetClient("1").Return(client)
|
|
||||||
_, err := c.Delete(context.Background(), &DeleteClientRequest{
|
|
||||||
ClientId: "1",
|
|
||||||
CleanSession: false,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClientService_Delete_CleanSession(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
cs := server.NewMockClientService(ctrl)
|
|
||||||
sr := server.NewMockStatsReader(ctrl)
|
|
||||||
|
|
||||||
admin := &Admin{
|
|
||||||
statsReader: sr,
|
|
||||||
clientService: cs,
|
|
||||||
store: newStore(sr, mockConfig),
|
|
||||||
}
|
|
||||||
c := &clientService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
cs.EXPECT().TerminateSession("1")
|
|
||||||
_, err := c.Delete(context.Background(), &DeleteClientRequest{
|
|
||||||
ClientId: "1",
|
|
||||||
CleanSession: true,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config is the configuration for the admin plugin.
|
|
||||||
type Config struct {
|
|
||||||
HTTP HTTPConfig `yaml:"http"`
|
|
||||||
GRPC GRPCConfig `yaml:"grpc"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPConfig is the configuration for http endpoint.
|
|
||||||
type HTTPConfig struct {
|
|
||||||
// Enable indicates whether to expose http endpoint.
|
|
||||||
Enable bool `yaml:"enable"`
|
|
||||||
// Addr is the address that the http server listen on.
|
|
||||||
Addr string `yaml:"http_addr"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GRPCConfig is the configuration for gRPC endpoint.
|
|
||||||
type GRPCConfig struct {
|
|
||||||
// Addr is the address that the gRPC server listen on.
|
|
||||||
Addr string `yaml:"http_addr"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the configuration, and return an error if it is invalid.
|
|
||||||
func (c *Config) Validate() error {
|
|
||||||
if c.HTTP.Enable {
|
|
||||||
_, _, err := net.SplitHostPort(c.HTTP.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("invalid http_addr")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, _, err := net.SplitHostPort(c.GRPC.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("invalid grpc_addr")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultConfig is the default configuration.
|
|
||||||
var DefaultConfig = Config{
|
|
||||||
HTTP: HTTPConfig{
|
|
||||||
Enable: true,
|
|
||||||
Addr: "127.0.0.1:57091",
|
|
||||||
},
|
|
||||||
GRPC: GRPCConfig{
|
|
||||||
Addr: "unix://./mqttd.sock",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
type cfg Config
|
|
||||||
var v = &struct {
|
|
||||||
Admin cfg `yaml:"admin"`
|
|
||||||
}{
|
|
||||||
Admin: cfg(DefaultConfig),
|
|
||||||
}
|
|
||||||
if err := unmarshal(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
emptyGRPC := GRPCConfig{}
|
|
||||||
if v.Admin.GRPC == emptyGRPC {
|
|
||||||
v.Admin.GRPC = DefaultConfig.GRPC
|
|
||||||
}
|
|
||||||
emptyHTTP := HTTPConfig{}
|
|
||||||
if v.Admin.HTTP == emptyHTTP {
|
|
||||||
v.Admin.HTTP = DefaultConfig.HTTP
|
|
||||||
}
|
|
||||||
empty := cfg(Config{})
|
|
||||||
if v.Admin == empty {
|
|
||||||
v.Admin = cfg(DefaultConfig)
|
|
||||||
}
|
|
||||||
*c = Config(v.Admin)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (a *Admin) HookWrapper() server.HookWrapper {
|
|
||||||
return server.HookWrapper{
|
|
||||||
OnSessionCreatedWrapper: a.OnSessionCreatedWrapper,
|
|
||||||
OnSessionResumedWrapper: a.OnSessionResumedWrapper,
|
|
||||||
OnClosedWrapper: a.OnClosedWrapper,
|
|
||||||
OnSessionTerminatedWrapper: a.OnSessionTerminatedWrapper,
|
|
||||||
OnSubscribedWrapper: a.OnSubscribedWrapper,
|
|
||||||
OnUnsubscribedWrapper: a.OnUnsubscribedWrapper,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) OnSessionCreatedWrapper(pre server.OnSessionCreated) server.OnSessionCreated {
|
|
||||||
return func(ctx context.Context, client server.Client) {
|
|
||||||
pre(ctx, client)
|
|
||||||
a.store.addClient(client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) OnSessionResumedWrapper(pre server.OnSessionResumed) server.OnSessionResumed {
|
|
||||||
return func(ctx context.Context, client server.Client) {
|
|
||||||
pre(ctx, client)
|
|
||||||
a.store.addClient(client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) OnClosedWrapper(pre server.OnClosed) server.OnClosed {
|
|
||||||
return func(ctx context.Context, client server.Client, err error) {
|
|
||||||
pre(ctx, client, err)
|
|
||||||
a.store.setClientDisconnected(client.ClientOptions().ClientID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) OnSessionTerminatedWrapper(pre server.OnSessionTerminated) server.OnSessionTerminated {
|
|
||||||
return func(ctx context.Context, clientID string, reason server.SessionTerminatedReason) {
|
|
||||||
pre(ctx, clientID, reason)
|
|
||||||
a.store.removeClient(clientID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) OnSubscribedWrapper(pre server.OnSubscribed) server.OnSubscribed {
|
|
||||||
return func(ctx context.Context, client server.Client, subscription *gmqtt.Subscription) {
|
|
||||||
pre(ctx, client, subscription)
|
|
||||||
a.store.addSubscription(client.ClientOptions().ClientID, subscription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) OnUnsubscribedWrapper(pre server.OnUnsubscribed) server.OnUnsubscribed {
|
|
||||||
return func(ctx context.Context, client server.Client, topicName string) {
|
|
||||||
pre(ctx, client, topicName)
|
|
||||||
a.store.removeSubscription(client.ClientOptions().ClientID, topicName)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package gmqtt.admin.api;
|
|
||||||
option go_package = ".;admin";
|
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
import "google/protobuf/empty.proto";
|
|
||||||
import "google/protobuf/timestamp.proto";
|
|
||||||
|
|
||||||
message ListClientRequest {
|
|
||||||
uint32 page_size = 1;
|
|
||||||
uint32 page = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ListClientResponse {
|
|
||||||
repeated Client clients = 1;
|
|
||||||
uint32 total_count = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetClientRequest {
|
|
||||||
string client_id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetClientResponse {
|
|
||||||
Client client = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
message DeleteClientRequest {
|
|
||||||
string client_id = 1;
|
|
||||||
bool clean_session = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Client {
|
|
||||||
string client_id = 1;
|
|
||||||
string username = 2;
|
|
||||||
int32 keep_alive = 3;
|
|
||||||
int32 version = 4;
|
|
||||||
string remote_addr = 5;
|
|
||||||
string local_addr = 6;
|
|
||||||
google.protobuf.Timestamp connected_at = 7;
|
|
||||||
google.protobuf.Timestamp disconnected_at = 8;
|
|
||||||
uint32 session_expiry = 9;
|
|
||||||
uint32 max_inflight = 10;
|
|
||||||
uint32 inflight_len = 11;
|
|
||||||
uint32 max_queue = 12;
|
|
||||||
uint32 queue_len = 13;
|
|
||||||
uint32 subscriptions_current = 14;
|
|
||||||
uint32 subscriptions_total = 15;
|
|
||||||
uint64 packets_received_bytes = 16;
|
|
||||||
uint64 packets_received_nums = 17;
|
|
||||||
uint64 packets_send_bytes = 18;
|
|
||||||
uint64 packets_send_nums = 19;
|
|
||||||
uint64 message_dropped = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
service ClientService {
|
|
||||||
// List clients
|
|
||||||
rpc List (ListClientRequest) returns (ListClientResponse){
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/clients"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Get the client for given client id.
|
|
||||||
// Return NotFound error when client not found.
|
|
||||||
rpc Get (GetClientRequest) returns (GetClientResponse){
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/clients/{client_id}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Disconnect the client for given client id.
|
|
||||||
rpc Delete (DeleteClientRequest) returns (google.protobuf.Empty) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
delete: "/v1/clients/{client_id}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
protoc -I. \
|
|
||||||
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway \
|
|
||||||
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
|
||||||
--go-grpc_out=../ \
|
|
||||||
--go_out=../ \
|
|
||||||
--grpc-gateway_out=../ \
|
|
||||||
--swagger_out=../swagger \
|
|
||||||
*.proto
|
|
@ -1,36 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package gmqtt.admin.api;
|
|
||||||
option go_package = ".;admin";
|
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
import "google/protobuf/empty.proto";
|
|
||||||
|
|
||||||
message PublishRequest {
|
|
||||||
string topic_name = 1;
|
|
||||||
string payload = 2;
|
|
||||||
uint32 qos = 3;
|
|
||||||
bool retained = 4;
|
|
||||||
// the following fields are using in v5 client.
|
|
||||||
string content_type = 5;
|
|
||||||
string correlation_data = 6;
|
|
||||||
uint32 message_expiry = 7;
|
|
||||||
uint32 payload_format = 8;
|
|
||||||
string response_topic = 9;
|
|
||||||
repeated UserProperties user_properties = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UserProperties {
|
|
||||||
bytes K = 1;
|
|
||||||
bytes V = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
service PublishService {
|
|
||||||
// Publish message to broker
|
|
||||||
rpc Publish (PublishRequest) returns (google.protobuf.Empty){
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/publish"
|
|
||||||
body:"*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package gmqtt.admin.api;
|
|
||||||
option go_package = ".;admin";
|
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
|
||||||
import "google/protobuf/empty.proto";
|
|
||||||
|
|
||||||
enum SubFilterType {
|
|
||||||
SUB_FILTER_TYPE_SYS_UNSPECIFIED = 0;
|
|
||||||
SUB_FILTER_TYPE_SYS = 1;
|
|
||||||
SUB_FILTER_TYPE_SHARED = 2;
|
|
||||||
SUB_FILTER_TYPE_NON_SHARED = 3;
|
|
||||||
}
|
|
||||||
enum SubMatchType {
|
|
||||||
SUB_MATCH_TYPE_MATCH_UNSPECIFIED = 0;
|
|
||||||
SUB_MATCH_TYPE_MATCH_NAME = 1;
|
|
||||||
SUB_MATCH_TYPE_MATCH_FILTER = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ListSubscriptionRequest {
|
|
||||||
uint32 page_size = 1;
|
|
||||||
uint32 page = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ListSubscriptionResponse {
|
|
||||||
repeated Subscription subscriptions = 1;
|
|
||||||
uint32 total_count = 2;
|
|
||||||
}
|
|
||||||
message FilterSubscriptionRequest {
|
|
||||||
// If set, only filter the subscriptions that belongs to the client.
|
|
||||||
string client_id = 1;
|
|
||||||
// filter_type indicates what kinds of topics are going to filter.
|
|
||||||
// If there are multiple types, use ',' to separate. e.g : 1,2
|
|
||||||
// There are 3 kinds of topic can be filtered, defined by SubFilterType:
|
|
||||||
// 1 = System Topic(begin with '$')
|
|
||||||
// 2 = Shared Topic
|
|
||||||
// 3 = NonShared Topic
|
|
||||||
string filter_type = 2;
|
|
||||||
// If 1 (SUB_MATCH_TYPE_MATCH_NAME), the server will return subscriptions which has the same topic name with request topic_name.
|
|
||||||
// If 2 (SUB_MATCH_TYPE_MATCH_FILTER),the server will return subscriptions which match the request topic_name .
|
|
||||||
// match_type must be set when filter_type is not empty.
|
|
||||||
SubMatchType match_type = 3;
|
|
||||||
// topic_name must be set when match_type is not zero.
|
|
||||||
string topic_name = 4;
|
|
||||||
// The maximum subscriptions can be returned.
|
|
||||||
int32 limit = 5;
|
|
||||||
}
|
|
||||||
message FilterSubscriptionResponse {
|
|
||||||
repeated Subscription subscriptions = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SubscribeRequest {
|
|
||||||
string client_id = 1;
|
|
||||||
repeated Subscription subscriptions = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SubscribeResponse {
|
|
||||||
// indicates whether it is a new subscription or the subscription is already existed.
|
|
||||||
repeated bool new = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UnsubscribeRequest {
|
|
||||||
string client_id = 1;
|
|
||||||
repeated string topics = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Subscription {
|
|
||||||
string topic_name = 1;
|
|
||||||
uint32 id = 2;
|
|
||||||
uint32 qos = 3;
|
|
||||||
bool no_local = 4;
|
|
||||||
bool retain_as_published = 5;
|
|
||||||
uint32 retain_handling = 6;
|
|
||||||
string client_id = 7;
|
|
||||||
}
|
|
||||||
service SubscriptionService {
|
|
||||||
// List subscriptions.
|
|
||||||
rpc List (ListSubscriptionRequest) returns (ListSubscriptionResponse){
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/subscriptions"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Filter subscriptions, paging is not supported in this API.
|
|
||||||
rpc Filter(FilterSubscriptionRequest) returns (FilterSubscriptionResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/v1/filter_subscriptions"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Subscribe topics for the client.
|
|
||||||
rpc Subscribe (SubscribeRequest) returns (SubscribeResponse) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/subscribe"
|
|
||||||
body:"*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Unsubscribe topics for the client.
|
|
||||||
rpc Unsubscribe (UnsubscribeRequest) returns (google.protobuf.Empty) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
post: "/v1/unsubscribe"
|
|
||||||
body:"*"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
type publisher struct {
|
|
||||||
a *Admin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *publisher) mustEmbedUnimplementedPublishServiceServer() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish publishes a message into broker.
|
|
||||||
func (p *publisher) Publish(ctx context.Context, req *PublishRequest) (resp *empty.Empty, err error) {
|
|
||||||
if !packets.ValidV5Topic([]byte(req.TopicName)) {
|
|
||||||
return nil, ErrInvalidArgument("topic_name", "")
|
|
||||||
}
|
|
||||||
if req.Qos > uint32(packets.Qos2) {
|
|
||||||
return nil, ErrInvalidArgument("qos", "")
|
|
||||||
}
|
|
||||||
if req.PayloadFormat != 0 && req.PayloadFormat != 1 {
|
|
||||||
return nil, ErrInvalidArgument("payload_format", "")
|
|
||||||
}
|
|
||||||
if req.ResponseTopic != "" && !packets.ValidV5Topic([]byte(req.ResponseTopic)) {
|
|
||||||
return nil, ErrInvalidArgument("response_topic", "")
|
|
||||||
}
|
|
||||||
var userPpt []packets.UserProperty
|
|
||||||
for _, v := range req.UserProperties {
|
|
||||||
userPpt = append(userPpt, packets.UserProperty{
|
|
||||||
K: v.K,
|
|
||||||
V: v.V,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
p.a.publisher.Publish(&mqttbroker.Message{
|
|
||||||
Dup: false,
|
|
||||||
QoS: byte(req.Qos),
|
|
||||||
Retained: req.Retained,
|
|
||||||
Topic: req.TopicName,
|
|
||||||
Payload: []byte(req.Payload),
|
|
||||||
ContentType: req.ContentType,
|
|
||||||
CorrelationData: []byte(req.CorrelationData),
|
|
||||||
MessageExpiry: req.MessageExpiry,
|
|
||||||
PayloadFormat: packets.PayloadFormat(req.PayloadFormat),
|
|
||||||
ResponseTopic: req.ResponseTopic,
|
|
||||||
UserProperties: userPpt,
|
|
||||||
})
|
|
||||||
return &empty.Empty{}, nil
|
|
||||||
}
|
|
@ -1,331 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.22.0
|
|
||||||
// protoc v3.13.0
|
|
||||||
// source: publish.proto
|
|
||||||
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
empty "github.com/golang/protobuf/ptypes/empty"
|
|
||||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
|
||||||
// of the legacy proto package is being used.
|
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
|
||||||
|
|
||||||
type PublishRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
TopicName string `protobuf:"bytes,1,opt,name=topic_name,json=topicName,proto3" json:"topic_name,omitempty"`
|
|
||||||
Payload string `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"`
|
|
||||||
Qos uint32 `protobuf:"varint,3,opt,name=qos,proto3" json:"qos,omitempty"`
|
|
||||||
Retained bool `protobuf:"varint,4,opt,name=retained,proto3" json:"retained,omitempty"`
|
|
||||||
// the following fields are using in v5 client.
|
|
||||||
ContentType string `protobuf:"bytes,5,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
|
||||||
CorrelationData string `protobuf:"bytes,6,opt,name=correlation_data,json=correlationData,proto3" json:"correlation_data,omitempty"`
|
|
||||||
MessageExpiry uint32 `protobuf:"varint,7,opt,name=message_expiry,json=messageExpiry,proto3" json:"message_expiry,omitempty"`
|
|
||||||
PayloadFormat uint32 `protobuf:"varint,8,opt,name=payload_format,json=payloadFormat,proto3" json:"payload_format,omitempty"`
|
|
||||||
ResponseTopic string `protobuf:"bytes,9,opt,name=response_topic,json=responseTopic,proto3" json:"response_topic,omitempty"`
|
|
||||||
UserProperties []*UserProperties `protobuf:"bytes,10,rep,name=user_properties,json=userProperties,proto3" json:"user_properties,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) Reset() {
|
|
||||||
*x = PublishRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_publish_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*PublishRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *PublishRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_publish_proto_msgTypes[0]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use PublishRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*PublishRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_publish_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetTopicName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.TopicName
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetPayload() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Payload
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetQos() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Qos
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetRetained() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.Retained
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetContentType() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ContentType
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetCorrelationData() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.CorrelationData
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetMessageExpiry() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.MessageExpiry
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetPayloadFormat() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.PayloadFormat
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetResponseTopic() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ResponseTopic
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PublishRequest) GetUserProperties() []*UserProperties {
|
|
||||||
if x != nil {
|
|
||||||
return x.UserProperties
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserProperties struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
K []byte `protobuf:"bytes,1,opt,name=K,proto3" json:"K,omitempty"`
|
|
||||||
V []byte `protobuf:"bytes,2,opt,name=V,proto3" json:"V,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProperties) Reset() {
|
|
||||||
*x = UserProperties{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_publish_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProperties) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*UserProperties) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *UserProperties) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_publish_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use UserProperties.ProtoReflect.Descriptor instead.
|
|
||||||
func (*UserProperties) Descriptor() ([]byte, []int) {
|
|
||||||
return file_publish_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProperties) GetK() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.K
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProperties) GetV() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.V
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_publish_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_publish_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
|
||||||
0x0f, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69,
|
|
||||||
0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e,
|
|
||||||
0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b,
|
|
||||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f,
|
|
||||||
0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x03, 0x0a, 0x0e,
|
|
||||||
0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d,
|
|
||||||
0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a,
|
|
||||||
0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
|
||||||
0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x71, 0x6f, 0x73, 0x18, 0x03,
|
|
||||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x71, 0x6f, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74,
|
|
||||||
0x61, 0x69, 0x6e, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x74,
|
|
||||||
0x61, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
|
||||||
0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e,
|
|
||||||
0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x72, 0x72,
|
|
||||||
0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44,
|
|
||||||
0x61, 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x65,
|
|
||||||
0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6d, 0x65, 0x73,
|
|
||||||
0x73, 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61,
|
|
||||||
0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01,
|
|
||||||
0x28, 0x0d, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61,
|
|
||||||
0x74, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x6f,
|
|
||||||
0x70, 0x69, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f,
|
|
||||||
0x6e, 0x73, 0x65, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x48, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72,
|
|
||||||
0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28,
|
|
||||||
0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e,
|
|
||||||
0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69,
|
|
||||||
0x65, 0x73, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69,
|
|
||||||
0x65, 0x73, 0x22, 0x2c, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72,
|
|
||||||
0x74, 0x69, 0x65, 0x73, 0x12, 0x0c, 0x0a, 0x01, 0x4b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
|
||||||
0x01, 0x4b, 0x12, 0x0c, 0x0a, 0x01, 0x56, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x56,
|
|
||||||
0x32, 0x6c, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69,
|
|
||||||
0x63, 0x65, 0x12, 0x5a, 0x0a, 0x07, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x1f, 0x2e,
|
|
||||||
0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e,
|
|
||||||
0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16,
|
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x22, 0x0b,
|
|
||||||
0x2f, 0x76, 0x31, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x3a, 0x01, 0x2a, 0x42, 0x09,
|
|
||||||
0x5a, 0x07, 0x2e, 0x3b, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_publish_proto_rawDescOnce sync.Once
|
|
||||||
file_publish_proto_rawDescData = file_publish_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_publish_proto_rawDescGZIP() []byte {
|
|
||||||
file_publish_proto_rawDescOnce.Do(func() {
|
|
||||||
file_publish_proto_rawDescData = protoimpl.X.CompressGZIP(file_publish_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_publish_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_publish_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
|
||||||
var file_publish_proto_goTypes = []interface{}{
|
|
||||||
(*PublishRequest)(nil), // 0: gmqtt.admin.api.PublishRequest
|
|
||||||
(*UserProperties)(nil), // 1: gmqtt.admin.api.UserProperties
|
|
||||||
(*empty.Empty)(nil), // 2: google.protobuf.Empty
|
|
||||||
}
|
|
||||||
var file_publish_proto_depIdxs = []int32{
|
|
||||||
1, // 0: gmqtt.admin.api.PublishRequest.user_properties:type_name -> gmqtt.admin.api.UserProperties
|
|
||||||
0, // 1: gmqtt.admin.api.PublishService.Publish:input_type -> gmqtt.admin.api.PublishRequest
|
|
||||||
2, // 2: gmqtt.admin.api.PublishService.Publish:output_type -> google.protobuf.Empty
|
|
||||||
2, // [2:3] is the sub-list for method output_type
|
|
||||||
1, // [1:2] is the sub-list for method input_type
|
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
|
||||||
0, // [0:1] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_publish_proto_init() }
|
|
||||||
func file_publish_proto_init() {
|
|
||||||
if File_publish_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_publish_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*PublishRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_publish_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*UserProperties); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_publish_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 2,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 1,
|
|
||||||
},
|
|
||||||
GoTypes: file_publish_proto_goTypes,
|
|
||||||
DependencyIndexes: file_publish_proto_depIdxs,
|
|
||||||
MessageInfos: file_publish_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_publish_proto = out.File
|
|
||||||
file_publish_proto_rawDesc = nil
|
|
||||||
file_publish_proto_goTypes = nil
|
|
||||||
file_publish_proto_depIdxs = nil
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
|
||||||
// source: publish.proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package admin is a reverse proxy.
|
|
||||||
|
|
||||||
It translates gRPC into RESTful JSON APIs.
|
|
||||||
*/
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/descriptor"
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Suppress "imported and not used" errors
|
|
||||||
var _ codes.Code
|
|
||||||
var _ io.Reader
|
|
||||||
var _ status.Status
|
|
||||||
var _ = runtime.String
|
|
||||||
var _ = utilities.NewDoubleArray
|
|
||||||
var _ = descriptor.ForMessage
|
|
||||||
|
|
||||||
func request_PublishService_Publish_0(ctx context.Context, marshaler runtime.Marshaler, client PublishServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq PublishRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.Publish(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_PublishService_Publish_0(ctx context.Context, marshaler runtime.Marshaler, server PublishServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq PublishRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.Publish(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPublishServiceHandlerServer registers the http handlers for service PublishService to "mux".
|
|
||||||
// UnaryRPC :call PublishServiceServer directly.
|
|
||||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
|
||||||
func RegisterPublishServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server PublishServiceServer) error {
|
|
||||||
|
|
||||||
mux.Handle("POST", pattern_PublishService_Publish_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_PublishService_Publish_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_PublishService_Publish_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPublishServiceHandlerFromEndpoint is same as RegisterPublishServiceHandler but
|
|
||||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
|
||||||
func RegisterPublishServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
|
||||||
conn, err := grpc.Dial(endpoint, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
if cerr := conn.Close(); cerr != nil {
|
|
||||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
if cerr := conn.Close(); cerr != nil {
|
|
||||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return RegisterPublishServiceHandler(ctx, mux, conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPublishServiceHandler registers the http handlers for service PublishService to "mux".
|
|
||||||
// The handlers forward requests to the grpc endpoint over "conn".
|
|
||||||
func RegisterPublishServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
|
||||||
return RegisterPublishServiceHandlerClient(ctx, mux, NewPublishServiceClient(conn))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPublishServiceHandlerClient registers the http handlers for service PublishService
|
|
||||||
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "PublishServiceClient".
|
|
||||||
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "PublishServiceClient"
|
|
||||||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
|
||||||
// "PublishServiceClient" to call the correct interceptors.
|
|
||||||
func RegisterPublishServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client PublishServiceClient) error {
|
|
||||||
|
|
||||||
mux.Handle("POST", pattern_PublishService_Publish_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_PublishService_Publish_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_PublishService_Publish_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
pattern_PublishService_Publish_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "publish"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
forward_PublishService_Publish_0 = runtime.ForwardResponseMessage
|
|
||||||
)
|
|
@ -1,101 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
|
||||||
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "context"
|
|
||||||
|
|
||||||
empty "github.com/golang/protobuf/ptypes/empty"
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
codes "google.golang.org/grpc/codes"
|
|
||||||
status "google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the grpc package it is being compiled against.
|
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
|
||||||
|
|
||||||
// PublishServiceClient is the client API for PublishService service.
|
|
||||||
//
|
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
|
||||||
type PublishServiceClient interface {
|
|
||||||
// Publish message to broker
|
|
||||||
Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*empty.Empty, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type publishServiceClient struct {
|
|
||||||
cc grpc.ClientConnInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPublishServiceClient(cc grpc.ClientConnInterface) PublishServiceClient {
|
|
||||||
return &publishServiceClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *publishServiceClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*empty.Empty, error) {
|
|
||||||
out := new(empty.Empty)
|
|
||||||
err := c.cc.Invoke(ctx, "/gmqtt.admin.api.PublishService/Publish", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublishServiceServer is the server API for PublishService service.
|
|
||||||
// All implementations must embed UnimplementedPublishServiceServer
|
|
||||||
// for forward compatibility
|
|
||||||
type PublishServiceServer interface {
|
|
||||||
// Publish message to broker
|
|
||||||
Publish(context.Context, *PublishRequest) (*empty.Empty, error)
|
|
||||||
mustEmbedUnimplementedPublishServiceServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedPublishServiceServer must be embedded to have forward compatible implementations.
|
|
||||||
type UnimplementedPublishServiceServer struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (UnimplementedPublishServiceServer) Publish(context.Context, *PublishRequest) (*empty.Empty, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedPublishServiceServer) mustEmbedUnimplementedPublishServiceServer() {}
|
|
||||||
|
|
||||||
// UnsafePublishServiceServer may be embedded to opt out of forward compatibility for this service.
|
|
||||||
// Use of this interface is not recommended, as added methods to PublishServiceServer will
|
|
||||||
// result in compilation errors.
|
|
||||||
type UnsafePublishServiceServer interface {
|
|
||||||
mustEmbedUnimplementedPublishServiceServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterPublishServiceServer(s grpc.ServiceRegistrar, srv PublishServiceServer) {
|
|
||||||
s.RegisterService(&_PublishService_serviceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _PublishService_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(PublishRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(PublishServiceServer).Publish(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/gmqtt.admin.api.PublishService/Publish",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(PublishServiceServer).Publish(ctx, req.(*PublishRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _PublishService_serviceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "gmqtt.admin.api.PublishService",
|
|
||||||
HandlerType: (*PublishServiceServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "Publish",
|
|
||||||
Handler: _PublishService_Publish_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{},
|
|
||||||
Metadata: "publish.proto",
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPublisher_Publish(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
mp := server.NewMockPublisher(ctrl)
|
|
||||||
pub := &publisher{
|
|
||||||
a: &Admin{
|
|
||||||
publisher: mp,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
msg := &gmqtt.Message{
|
|
||||||
QoS: 1,
|
|
||||||
Retained: true,
|
|
||||||
Topic: "topic",
|
|
||||||
Payload: []byte("abc"),
|
|
||||||
ContentType: "ct",
|
|
||||||
CorrelationData: []byte("co"),
|
|
||||||
MessageExpiry: 1,
|
|
||||||
PayloadFormat: 1,
|
|
||||||
ResponseTopic: "resp",
|
|
||||||
UserProperties: []packets.UserProperty{
|
|
||||||
{
|
|
||||||
K: []byte("K"),
|
|
||||||
V: []byte("V"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
mp.EXPECT().Publish(msg)
|
|
||||||
_, err := pub.Publish(context.Background(), &PublishRequest{
|
|
||||||
TopicName: msg.Topic,
|
|
||||||
Payload: string(msg.Payload),
|
|
||||||
Qos: uint32(msg.QoS),
|
|
||||||
Retained: msg.Retained,
|
|
||||||
ContentType: msg.ContentType,
|
|
||||||
CorrelationData: string(msg.CorrelationData),
|
|
||||||
MessageExpiry: msg.MessageExpiry,
|
|
||||||
PayloadFormat: uint32(msg.PayloadFormat),
|
|
||||||
ResponseTopic: msg.ResponseTopic,
|
|
||||||
UserProperties: []*UserProperties{
|
|
||||||
{
|
|
||||||
K: []byte("K"),
|
|
||||||
V: []byte("V"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublisher_Publish_InvalidArgument(t *testing.T) {
|
|
||||||
var tt = []struct {
|
|
||||||
name string
|
|
||||||
field string
|
|
||||||
req *PublishRequest
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "invalid_topic_name",
|
|
||||||
field: "topic_name",
|
|
||||||
req: &PublishRequest{
|
|
||||||
TopicName: "$share/a",
|
|
||||||
Qos: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid_qos",
|
|
||||||
field: "qos",
|
|
||||||
req: &PublishRequest{
|
|
||||||
TopicName: "a",
|
|
||||||
Qos: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid_payload_format",
|
|
||||||
field: "payload_format",
|
|
||||||
req: &PublishRequest{
|
|
||||||
TopicName: "a",
|
|
||||||
Qos: 2,
|
|
||||||
PayloadFormat: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid_response_topic",
|
|
||||||
field: "response_topic",
|
|
||||||
req: &PublishRequest{
|
|
||||||
TopicName: "a",
|
|
||||||
Qos: 2,
|
|
||||||
PayloadFormat: 1,
|
|
||||||
ResponseTopic: "#/",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
t.Run(v.name, func(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
mp := server.NewMockPublisher(ctrl)
|
|
||||||
pub := &publisher{
|
|
||||||
a: &Admin{
|
|
||||||
publisher: mp,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, err := pub.Publish(context.Background(), v.req)
|
|
||||||
s, ok := status.FromError(err)
|
|
||||||
a.True(ok)
|
|
||||||
a.Contains(s.Message(), v.field)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,159 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
type store struct {
|
|
||||||
clientMu sync.RWMutex
|
|
||||||
clientIndexer *Indexer
|
|
||||||
subMu sync.RWMutex
|
|
||||||
subIndexer *Indexer
|
|
||||||
config config.Config
|
|
||||||
statsReader server.StatsReader
|
|
||||||
subscriptionService server.SubscriptionService
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStore(statsReader server.StatsReader, config config.Config) *store {
|
|
||||||
return &store{
|
|
||||||
clientIndexer: NewIndexer(),
|
|
||||||
subIndexer: NewIndexer(),
|
|
||||||
statsReader: statsReader,
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *store) addSubscription(clientID string, sub *gmqtt.Subscription) {
|
|
||||||
s.subMu.Lock()
|
|
||||||
defer s.subMu.Unlock()
|
|
||||||
|
|
||||||
subInfo := &Subscription{
|
|
||||||
TopicName: sub.GetFullTopicName(),
|
|
||||||
Id: sub.ID,
|
|
||||||
Qos: uint32(sub.QoS),
|
|
||||||
NoLocal: sub.NoLocal,
|
|
||||||
RetainAsPublished: sub.RetainAsPublished,
|
|
||||||
RetainHandling: uint32(sub.RetainHandling),
|
|
||||||
ClientId: clientID,
|
|
||||||
}
|
|
||||||
key := clientID + "_" + sub.GetFullTopicName()
|
|
||||||
s.subIndexer.Set(key, subInfo)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *store) removeSubscription(clientID string, topicName string) {
|
|
||||||
s.subMu.Lock()
|
|
||||||
defer s.subMu.Unlock()
|
|
||||||
s.subIndexer.Remove(clientID + "_" + topicName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *store) addClient(client server.Client) {
|
|
||||||
c := newClientInfo(client, uint32(s.config.MQTT.MaxQueuedMsg))
|
|
||||||
s.clientMu.Lock()
|
|
||||||
s.clientIndexer.Set(c.ClientId, c)
|
|
||||||
s.clientMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *store) setClientDisconnected(clientID string) {
|
|
||||||
s.clientMu.Lock()
|
|
||||||
defer s.clientMu.Unlock()
|
|
||||||
l := s.clientIndexer.GetByID(clientID)
|
|
||||||
if l == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l.Value.(*Client).DisconnectedAt = timestamppb.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *store) removeClient(clientID string) {
|
|
||||||
s.clientMu.Lock()
|
|
||||||
s.clientIndexer.Remove(clientID)
|
|
||||||
s.clientMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientByID returns the client information for the given client id.
|
|
||||||
func (s *store) GetClientByID(clientID string) *Client {
|
|
||||||
s.clientMu.RLock()
|
|
||||||
defer s.clientMu.RUnlock()
|
|
||||||
c := s.getClientByIDLocked(clientID)
|
|
||||||
fillClientInfo(c, s.statsReader)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClientInfo(client server.Client, maxQueue uint32) *Client {
|
|
||||||
clientOptions := client.ClientOptions()
|
|
||||||
rs := &Client{
|
|
||||||
ClientId: clientOptions.ClientID,
|
|
||||||
Username: clientOptions.Username,
|
|
||||||
KeepAlive: int32(clientOptions.KeepAlive),
|
|
||||||
Version: int32(client.Version()),
|
|
||||||
RemoteAddr: client.Connection().RemoteAddr().String(),
|
|
||||||
LocalAddr: client.Connection().LocalAddr().String(),
|
|
||||||
ConnectedAt: timestamppb.New(client.ConnectedAt()),
|
|
||||||
DisconnectedAt: nil,
|
|
||||||
SessionExpiry: clientOptions.SessionExpiry,
|
|
||||||
MaxInflight: uint32(clientOptions.MaxInflight),
|
|
||||||
MaxQueue: maxQueue,
|
|
||||||
}
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *store) getClientByIDLocked(clientID string) *Client {
|
|
||||||
if i := s.clientIndexer.GetByID(clientID); i != nil {
|
|
||||||
return i.Value.(*Client)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fillClientInfo(c *Client, stsReader server.StatsReader) {
|
|
||||||
if c == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sts, ok := stsReader.GetClientStats(c.ClientId)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.SubscriptionsCurrent = uint32(sts.SubscriptionStats.SubscriptionsCurrent)
|
|
||||||
c.SubscriptionsTotal = uint32(sts.SubscriptionStats.SubscriptionsTotal)
|
|
||||||
c.PacketsReceivedBytes = sts.PacketStats.BytesReceived.Total
|
|
||||||
c.PacketsReceivedNums = sts.PacketStats.ReceivedTotal.Total
|
|
||||||
c.PacketsSendBytes = sts.PacketStats.BytesSent.Total
|
|
||||||
c.PacketsSendNums = sts.PacketStats.SentTotal.Total
|
|
||||||
c.MessageDropped = sts.MessageStats.GetDroppedTotal()
|
|
||||||
c.InflightLen = uint32(sts.MessageStats.InflightCurrent)
|
|
||||||
c.QueueLen = uint32(sts.MessageStats.QueuedCurrent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClients
|
|
||||||
func (s *store) GetClients(page, pageSize uint) (rs []*Client, total uint32, err error) {
|
|
||||||
rs = make([]*Client, 0)
|
|
||||||
fn := func(elem *list.Element) {
|
|
||||||
c := elem.Value.(*Client)
|
|
||||||
fillClientInfo(c, s.statsReader)
|
|
||||||
rs = append(rs, elem.Value.(*Client))
|
|
||||||
}
|
|
||||||
s.clientMu.RLock()
|
|
||||||
defer s.clientMu.RUnlock()
|
|
||||||
offset, n := GetOffsetN(page, pageSize)
|
|
||||||
s.clientIndexer.Iterate(fn, offset, n)
|
|
||||||
return rs, uint32(s.clientIndexer.Len()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSubscriptions
|
|
||||||
func (s *store) GetSubscriptions(page, pageSize uint) (rs []*Subscription, total uint32, err error) {
|
|
||||||
rs = make([]*Subscription, 0)
|
|
||||||
fn := func(elem *list.Element) {
|
|
||||||
rs = append(rs, elem.Value.(*Subscription))
|
|
||||||
}
|
|
||||||
s.subMu.RLock()
|
|
||||||
defer s.subMu.RUnlock()
|
|
||||||
offset, n := GetOffsetN(page, pageSize)
|
|
||||||
s.subIndexer.Iterate(fn, offset, n)
|
|
||||||
return rs, uint32(s.subIndexer.Len()), nil
|
|
||||||
}
|
|
@ -1,182 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription"
|
|
||||||
"github.com/winc-link/hummingbird/internal/pkg/packets"
|
|
||||||
)
|
|
||||||
|
|
||||||
type subscriptionService struct {
|
|
||||||
a *Admin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *subscriptionService) mustEmbedUnimplementedSubscriptionServiceServer() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// List lists subscriptions in the broker.
|
|
||||||
func (s *subscriptionService) List(ctx context.Context, req *ListSubscriptionRequest) (*ListSubscriptionResponse, error) {
|
|
||||||
page, pageSize := GetPage(req.Page, req.PageSize)
|
|
||||||
subs, total, err := s.a.store.GetSubscriptions(page, pageSize)
|
|
||||||
if err != nil {
|
|
||||||
return &ListSubscriptionResponse{}, err
|
|
||||||
}
|
|
||||||
return &ListSubscriptionResponse{
|
|
||||||
Subscriptions: subs,
|
|
||||||
TotalCount: total,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter filters subscriptions with the request params.
|
|
||||||
// Paging is not supported, and the results are not sorted in any way.
|
|
||||||
// Using huge req.Limit can impact performance.
|
|
||||||
func (s *subscriptionService) Filter(ctx context.Context, req *FilterSubscriptionRequest) (resp *FilterSubscriptionResponse, err error) {
|
|
||||||
var iterType subscription.IterationType
|
|
||||||
iterOpts := subscription.IterationOptions{
|
|
||||||
ClientID: req.ClientId,
|
|
||||||
TopicName: req.TopicName,
|
|
||||||
}
|
|
||||||
if req.FilterType == "" {
|
|
||||||
iterType = subscription.TypeAll
|
|
||||||
} else {
|
|
||||||
types := strings.Split(req.FilterType, ",")
|
|
||||||
for _, v := range types {
|
|
||||||
if v == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
i, err := strconv.Atoi(v)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, ErrInvalidArgument("filter_type", err.Error())
|
|
||||||
}
|
|
||||||
switch SubFilterType(i) {
|
|
||||||
|
|
||||||
case SubFilterType_SUB_FILTER_TYPE_SYS:
|
|
||||||
iterType |= subscription.TypeSYS
|
|
||||||
case SubFilterType_SUB_FILTER_TYPE_SHARED:
|
|
||||||
iterType |= subscription.TypeShared
|
|
||||||
case SubFilterType_SUB_FILTER_TYPE_NON_SHARED:
|
|
||||||
iterType |= subscription.TypeNonShared
|
|
||||||
default:
|
|
||||||
return nil, ErrInvalidArgument("filter_type", "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iterOpts.Type = iterType
|
|
||||||
|
|
||||||
if req.MatchType == SubMatchType_SUB_MATCH_TYPE_MATCH_NAME {
|
|
||||||
iterOpts.MatchType = subscription.MatchName
|
|
||||||
} else if req.MatchType == SubMatchType_SUB_MATCH_TYPE_MATCH_FILTER {
|
|
||||||
iterOpts.MatchType = subscription.MatchFilter
|
|
||||||
}
|
|
||||||
if iterOpts.TopicName == "" && iterOpts.MatchType != 0 {
|
|
||||||
return nil, ErrInvalidArgument("topic_name", "cannot be empty while match_type Set")
|
|
||||||
}
|
|
||||||
if iterOpts.TopicName != "" && iterOpts.MatchType == 0 {
|
|
||||||
return nil, ErrInvalidArgument("match_type", "cannot be empty while topic_name Set")
|
|
||||||
}
|
|
||||||
if iterOpts.TopicName != "" {
|
|
||||||
if !packets.ValidV5Topic([]byte(iterOpts.TopicName)) {
|
|
||||||
return nil, ErrInvalidArgument("topic_name", "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Limit > 1000 {
|
|
||||||
return nil, ErrInvalidArgument("limit", fmt.Sprintf("limit too large, must <= 1000"))
|
|
||||||
}
|
|
||||||
if req.Limit == 0 {
|
|
||||||
req.Limit = 20
|
|
||||||
}
|
|
||||||
resp = &FilterSubscriptionResponse{
|
|
||||||
Subscriptions: make([]*Subscription, 0),
|
|
||||||
}
|
|
||||||
i := int32(0)
|
|
||||||
s.a.store.subscriptionService.Iterate(func(clientID string, sub *gmqtt.Subscription) bool {
|
|
||||||
if i != req.Limit {
|
|
||||||
resp.Subscriptions = append(resp.Subscriptions, &Subscription{
|
|
||||||
TopicName: subscription.GetFullTopicName(sub.ShareName, sub.TopicFilter),
|
|
||||||
Id: sub.ID,
|
|
||||||
Qos: uint32(sub.QoS),
|
|
||||||
NoLocal: sub.NoLocal,
|
|
||||||
RetainAsPublished: sub.RetainAsPublished,
|
|
||||||
RetainHandling: uint32(sub.RetainHandling),
|
|
||||||
ClientId: clientID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
return true
|
|
||||||
}, iterOpts)
|
|
||||||
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe makes subscriptions for the given client.
|
|
||||||
func (s *subscriptionService) Subscribe(ctx context.Context, req *SubscribeRequest) (resp *SubscribeResponse, err error) {
|
|
||||||
if req.ClientId == "" {
|
|
||||||
return nil, ErrInvalidArgument("client_id", "cannot be empty")
|
|
||||||
}
|
|
||||||
if len(req.Subscriptions) == 0 {
|
|
||||||
return nil, ErrInvalidArgument("subIndexer", "zero length subIndexer")
|
|
||||||
}
|
|
||||||
var subs []*gmqtt.Subscription
|
|
||||||
for k, v := range req.Subscriptions {
|
|
||||||
shareName, name := subscription.SplitTopic(v.TopicName)
|
|
||||||
sub := &gmqtt.Subscription{
|
|
||||||
ShareName: shareName,
|
|
||||||
TopicFilter: name,
|
|
||||||
ID: v.Id,
|
|
||||||
QoS: uint8(v.Qos),
|
|
||||||
NoLocal: v.NoLocal,
|
|
||||||
RetainAsPublished: v.RetainAsPublished,
|
|
||||||
RetainHandling: byte(v.RetainHandling),
|
|
||||||
}
|
|
||||||
err := sub.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return nil, ErrInvalidArgument(fmt.Sprintf("subIndexer[%d]", k), err.Error())
|
|
||||||
}
|
|
||||||
subs = append(subs, sub)
|
|
||||||
}
|
|
||||||
rs, err := s.a.store.subscriptionService.Subscribe(req.ClientId, subs...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Internal, "failed to subscribe: %s", err.Error())
|
|
||||||
}
|
|
||||||
resp = &SubscribeResponse{
|
|
||||||
New: make([]bool, 0),
|
|
||||||
}
|
|
||||||
for _, v := range rs {
|
|
||||||
resp.New = append(resp.New, !v.AlreadyExisted)
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe unsubscribe topic for the given client.
|
|
||||||
func (s *subscriptionService) Unsubscribe(ctx context.Context, req *UnsubscribeRequest) (resp *empty.Empty, err error) {
|
|
||||||
if req.ClientId == "" {
|
|
||||||
return nil, ErrInvalidArgument("client_id", "cannot be empty")
|
|
||||||
}
|
|
||||||
if len(req.Topics) == 0 {
|
|
||||||
return nil, ErrInvalidArgument("topics", "zero length topics")
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range req.Topics {
|
|
||||||
if !packets.ValidV5Topic([]byte(v)) {
|
|
||||||
return nil, ErrInvalidArgument(fmt.Sprintf("topics[%d]", k), "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = s.a.store.subscriptionService.Unsubscribe(req.ClientId, req.Topics...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to unsubscribe: %s", err.Error()))
|
|
||||||
}
|
|
||||||
return &empty.Empty{}, nil
|
|
||||||
}
|
|
@ -1,922 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.22.0
|
|
||||||
// protoc v3.13.0
|
|
||||||
// source: subscription.proto
|
|
||||||
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
empty "github.com/golang/protobuf/ptypes/empty"
|
|
||||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
|
||||||
// of the legacy proto package is being used.
|
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
|
||||||
|
|
||||||
type SubFilterType int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
SubFilterType_SUB_FILTER_TYPE_SYS_UNSPECIFIED SubFilterType = 0
|
|
||||||
SubFilterType_SUB_FILTER_TYPE_SYS SubFilterType = 1
|
|
||||||
SubFilterType_SUB_FILTER_TYPE_SHARED SubFilterType = 2
|
|
||||||
SubFilterType_SUB_FILTER_TYPE_NON_SHARED SubFilterType = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enum value maps for SubFilterType.
|
|
||||||
var (
|
|
||||||
SubFilterType_name = map[int32]string{
|
|
||||||
0: "SUB_FILTER_TYPE_SYS_UNSPECIFIED",
|
|
||||||
1: "SUB_FILTER_TYPE_SYS",
|
|
||||||
2: "SUB_FILTER_TYPE_SHARED",
|
|
||||||
3: "SUB_FILTER_TYPE_NON_SHARED",
|
|
||||||
}
|
|
||||||
SubFilterType_value = map[string]int32{
|
|
||||||
"SUB_FILTER_TYPE_SYS_UNSPECIFIED": 0,
|
|
||||||
"SUB_FILTER_TYPE_SYS": 1,
|
|
||||||
"SUB_FILTER_TYPE_SHARED": 2,
|
|
||||||
"SUB_FILTER_TYPE_NON_SHARED": 3,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (x SubFilterType) Enum() *SubFilterType {
|
|
||||||
p := new(SubFilterType)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x SubFilterType) String() string {
|
|
||||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (SubFilterType) Descriptor() protoreflect.EnumDescriptor {
|
|
||||||
return file_subscription_proto_enumTypes[0].Descriptor()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (SubFilterType) Type() protoreflect.EnumType {
|
|
||||||
return &file_subscription_proto_enumTypes[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x SubFilterType) Number() protoreflect.EnumNumber {
|
|
||||||
return protoreflect.EnumNumber(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use SubFilterType.Descriptor instead.
|
|
||||||
func (SubFilterType) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubMatchType int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
SubMatchType_SUB_MATCH_TYPE_MATCH_UNSPECIFIED SubMatchType = 0
|
|
||||||
SubMatchType_SUB_MATCH_TYPE_MATCH_NAME SubMatchType = 1
|
|
||||||
SubMatchType_SUB_MATCH_TYPE_MATCH_FILTER SubMatchType = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enum value maps for SubMatchType.
|
|
||||||
var (
|
|
||||||
SubMatchType_name = map[int32]string{
|
|
||||||
0: "SUB_MATCH_TYPE_MATCH_UNSPECIFIED",
|
|
||||||
1: "SUB_MATCH_TYPE_MATCH_NAME",
|
|
||||||
2: "SUB_MATCH_TYPE_MATCH_FILTER",
|
|
||||||
}
|
|
||||||
SubMatchType_value = map[string]int32{
|
|
||||||
"SUB_MATCH_TYPE_MATCH_UNSPECIFIED": 0,
|
|
||||||
"SUB_MATCH_TYPE_MATCH_NAME": 1,
|
|
||||||
"SUB_MATCH_TYPE_MATCH_FILTER": 2,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (x SubMatchType) Enum() *SubMatchType {
|
|
||||||
p := new(SubMatchType)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x SubMatchType) String() string {
|
|
||||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (SubMatchType) Descriptor() protoreflect.EnumDescriptor {
|
|
||||||
return file_subscription_proto_enumTypes[1].Descriptor()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (SubMatchType) Type() protoreflect.EnumType {
|
|
||||||
return &file_subscription_proto_enumTypes[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x SubMatchType) Number() protoreflect.EnumNumber {
|
|
||||||
return protoreflect.EnumNumber(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use SubMatchType.Descriptor instead.
|
|
||||||
func (SubMatchType) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListSubscriptionRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
PageSize uint32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
|
|
||||||
Page uint32 `protobuf:"varint,2,opt,name=page,proto3" json:"page,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionRequest) Reset() {
|
|
||||||
*x = ListSubscriptionRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_subscription_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ListSubscriptionRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_subscription_proto_msgTypes[0]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ListSubscriptionRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*ListSubscriptionRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionRequest) GetPageSize() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.PageSize
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionRequest) GetPage() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Page
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListSubscriptionResponse struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Subscriptions []*Subscription `protobuf:"bytes,1,rep,name=subscriptions,proto3" json:"subscriptions,omitempty"`
|
|
||||||
TotalCount uint32 `protobuf:"varint,2,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionResponse) Reset() {
|
|
||||||
*x = ListSubscriptionResponse{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_subscription_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionResponse) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ListSubscriptionResponse) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionResponse) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_subscription_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ListSubscriptionResponse.ProtoReflect.Descriptor instead.
|
|
||||||
func (*ListSubscriptionResponse) Descriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionResponse) GetSubscriptions() []*Subscription {
|
|
||||||
if x != nil {
|
|
||||||
return x.Subscriptions
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ListSubscriptionResponse) GetTotalCount() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.TotalCount
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type FilterSubscriptionRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
// If set, only filter the subscriptions that belongs to the client.
|
|
||||||
ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
|
||||||
// filter_type indicates what kinds of topics are going to filter.
|
|
||||||
// If there are multiple types, use ',' to separate. e.g : 1,2
|
|
||||||
// There are 3 kinds of topic can be filtered, defined by SubFilterType:
|
|
||||||
// 1 = System Topic(begin with '$')
|
|
||||||
// 2 = Shared Topic
|
|
||||||
// 3 = NonShared Topic
|
|
||||||
FilterType string `protobuf:"bytes,2,opt,name=filter_type,json=filterType,proto3" json:"filter_type,omitempty"`
|
|
||||||
// If 1 (SUB_MATCH_TYPE_MATCH_NAME), the server will return subscriptions which has the same topic name with request topic_name.
|
|
||||||
// If 2 (SUB_MATCH_TYPE_MATCH_FILTER),the server will return subscriptions which match the request topic_name .
|
|
||||||
// match_type must be set when filter_type is not empty.
|
|
||||||
MatchType SubMatchType `protobuf:"varint,3,opt,name=match_type,json=matchType,proto3,enum=gmqtt.admin.api.SubMatchType" json:"match_type,omitempty"`
|
|
||||||
// topic_name must be set when match_type is not zero.
|
|
||||||
TopicName string `protobuf:"bytes,4,opt,name=topic_name,json=topicName,proto3" json:"topic_name,omitempty"`
|
|
||||||
// The maximum subscriptions can be returned.
|
|
||||||
Limit int32 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionRequest) Reset() {
|
|
||||||
*x = FilterSubscriptionRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_subscription_proto_msgTypes[2]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*FilterSubscriptionRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_subscription_proto_msgTypes[2]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use FilterSubscriptionRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*FilterSubscriptionRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionRequest) GetClientId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ClientId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionRequest) GetFilterType() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.FilterType
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionRequest) GetMatchType() SubMatchType {
|
|
||||||
if x != nil {
|
|
||||||
return x.MatchType
|
|
||||||
}
|
|
||||||
return SubMatchType_SUB_MATCH_TYPE_MATCH_UNSPECIFIED
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionRequest) GetTopicName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.TopicName
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionRequest) GetLimit() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Limit
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type FilterSubscriptionResponse struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Subscriptions []*Subscription `protobuf:"bytes,1,rep,name=subscriptions,proto3" json:"subscriptions,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionResponse) Reset() {
|
|
||||||
*x = FilterSubscriptionResponse{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_subscription_proto_msgTypes[3]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionResponse) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*FilterSubscriptionResponse) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionResponse) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_subscription_proto_msgTypes[3]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use FilterSubscriptionResponse.ProtoReflect.Descriptor instead.
|
|
||||||
func (*FilterSubscriptionResponse) Descriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FilterSubscriptionResponse) GetSubscriptions() []*Subscription {
|
|
||||||
if x != nil {
|
|
||||||
return x.Subscriptions
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubscribeRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
|
||||||
Subscriptions []*Subscription `protobuf:"bytes,2,rep,name=subscriptions,proto3" json:"subscriptions,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeRequest) Reset() {
|
|
||||||
*x = SubscribeRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_subscription_proto_msgTypes[4]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SubscribeRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *SubscribeRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_subscription_proto_msgTypes[4]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{4}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeRequest) GetClientId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ClientId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeRequest) GetSubscriptions() []*Subscription {
|
|
||||||
if x != nil {
|
|
||||||
return x.Subscriptions
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubscribeResponse struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
// indicates whether it is a new subscription or the subscription is already existed.
|
|
||||||
New []bool `protobuf:"varint,1,rep,packed,name=new,proto3" json:"new,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeResponse) Reset() {
|
|
||||||
*x = SubscribeResponse{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_subscription_proto_msgTypes[5]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeResponse) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SubscribeResponse) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *SubscribeResponse) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_subscription_proto_msgTypes[5]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use SubscribeResponse.ProtoReflect.Descriptor instead.
|
|
||||||
func (*SubscribeResponse) Descriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{5}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeResponse) GetNew() []bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.New
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnsubscribeRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
|
||||||
Topics []string `protobuf:"bytes,2,rep,name=topics,proto3" json:"topics,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UnsubscribeRequest) Reset() {
|
|
||||||
*x = UnsubscribeRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_subscription_proto_msgTypes[6]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UnsubscribeRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*UnsubscribeRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *UnsubscribeRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_subscription_proto_msgTypes[6]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use UnsubscribeRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*UnsubscribeRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{6}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UnsubscribeRequest) GetClientId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ClientId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UnsubscribeRequest) GetTopics() []string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Topics
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Subscription struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
TopicName string `protobuf:"bytes,1,opt,name=topic_name,json=topicName,proto3" json:"topic_name,omitempty"`
|
|
||||||
Id uint32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"`
|
|
||||||
Qos uint32 `protobuf:"varint,3,opt,name=qos,proto3" json:"qos,omitempty"`
|
|
||||||
NoLocal bool `protobuf:"varint,4,opt,name=no_local,json=noLocal,proto3" json:"no_local,omitempty"`
|
|
||||||
RetainAsPublished bool `protobuf:"varint,5,opt,name=retain_as_published,json=retainAsPublished,proto3" json:"retain_as_published,omitempty"`
|
|
||||||
RetainHandling uint32 `protobuf:"varint,6,opt,name=retain_handling,json=retainHandling,proto3" json:"retain_handling,omitempty"`
|
|
||||||
ClientId string `protobuf:"bytes,7,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) Reset() {
|
|
||||||
*x = Subscription{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_subscription_proto_msgTypes[7]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Subscription) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Subscription) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_subscription_proto_msgTypes[7]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Subscription.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Subscription) Descriptor() ([]byte, []int) {
|
|
||||||
return file_subscription_proto_rawDescGZIP(), []int{7}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) GetTopicName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.TopicName
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) GetId() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Id
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) GetQos() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Qos
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) GetNoLocal() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.NoLocal
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) GetRetainAsPublished() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.RetainAsPublished
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) GetRetainHandling() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.RetainHandling
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Subscription) GetClientId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ClientId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_subscription_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_subscription_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x12, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70,
|
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69,
|
|
||||||
0x6e, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70,
|
|
||||||
0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
|
||||||
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x22, 0x4a, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
|
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70,
|
|
||||||
0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08,
|
|
||||||
0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65,
|
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x22, 0x80, 0x01, 0x0a,
|
|
||||||
0x18, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
|
|
||||||
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x73, 0x75, 0x62,
|
|
||||||
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
|
||||||
0x32, 0x1d, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61,
|
|
||||||
0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52,
|
|
||||||
0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f,
|
|
||||||
0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20,
|
|
||||||
0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22,
|
|
||||||
0xcc, 0x01, 0x0a, 0x19, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72,
|
|
||||||
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a,
|
|
||||||
0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
|
||||||
0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69,
|
|
||||||
0x6c, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
|
||||||
0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x6d,
|
|
||||||
0x61, 0x74, 0x63, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
|
||||||
0x1d, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70,
|
|
||||||
0x69, 0x2e, 0x53, 0x75, 0x62, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09,
|
|
||||||
0x6d, 0x61, 0x74, 0x63, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70,
|
|
||||||
0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74,
|
|
||||||
0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69,
|
|
||||||
0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x61,
|
|
||||||
0x0a, 0x1a, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
|
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0d,
|
|
||||||
0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20,
|
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69,
|
|
||||||
0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
|
|
||||||
0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
|
||||||
0x73, 0x22, 0x74, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65,
|
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f,
|
|
||||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
|
||||||
0x49, 0x64, 0x12, 0x43, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
|
|
||||||
0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6d, 0x71, 0x74,
|
|
||||||
0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73,
|
|
||||||
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72,
|
|
||||||
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x25, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63,
|
|
||||||
0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03,
|
|
||||||
0x6e, 0x65, 0x77, 0x18, 0x01, 0x20, 0x03, 0x28, 0x08, 0x52, 0x03, 0x6e, 0x65, 0x77, 0x22, 0x49,
|
|
||||||
0x0a, 0x12, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71,
|
|
||||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69,
|
|
||||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49,
|
|
||||||
0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
|
||||||
0x09, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x22, 0xe0, 0x01, 0x0a, 0x0c, 0x53, 0x75,
|
|
||||||
0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f,
|
|
||||||
0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
|
|
||||||
0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
|
||||||
0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x71, 0x6f, 0x73,
|
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x71, 0x6f, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6e,
|
|
||||||
0x6f, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6e,
|
|
||||||
0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x74, 0x61, 0x69, 0x6e,
|
|
||||||
0x5f, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20,
|
|
||||||
0x01, 0x28, 0x08, 0x52, 0x11, 0x72, 0x65, 0x74, 0x61, 0x69, 0x6e, 0x41, 0x73, 0x50, 0x75, 0x62,
|
|
||||||
0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x61, 0x69, 0x6e,
|
|
||||||
0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
|
||||||
0x0e, 0x72, 0x65, 0x74, 0x61, 0x69, 0x6e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x69, 0x6e, 0x67, 0x12,
|
|
||||||
0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x2a, 0x89, 0x01, 0x0a,
|
|
||||||
0x0d, 0x53, 0x75, 0x62, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23,
|
|
||||||
0x0a, 0x1f, 0x53, 0x55, 0x42, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50,
|
|
||||||
0x45, 0x5f, 0x53, 0x59, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
|
|
||||||
0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x55, 0x42, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45,
|
|
||||||
0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16,
|
|
||||||
0x53, 0x55, 0x42, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
|
|
||||||
0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x55, 0x42, 0x5f,
|
|
||||||
0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f,
|
|
||||||
0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x74, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x4d,
|
|
||||||
0x61, 0x74, 0x63, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x20, 0x53, 0x55, 0x42, 0x5f,
|
|
||||||
0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48,
|
|
||||||
0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d,
|
|
||||||
0x0a, 0x19, 0x53, 0x55, 0x42, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x54, 0x59, 0x50, 0x45,
|
|
||||||
0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x1f, 0x0a,
|
|
||||||
0x1b, 0x53, 0x55, 0x42, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
|
|
||||||
0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x10, 0x02, 0x32, 0xe9,
|
|
||||||
0x03, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53,
|
|
||||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x76, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x28,
|
|
||||||
0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69,
|
|
||||||
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
|
|
||||||
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74,
|
|
||||||
0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53,
|
|
||||||
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
|
||||||
0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31,
|
|
||||||
0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x83,
|
|
||||||
0x01, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2a, 0x2e, 0x67, 0x6d, 0x71, 0x74,
|
|
||||||
0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, 0x74,
|
|
||||||
0x65, 0x72, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
|
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64,
|
|
||||||
0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x75,
|
|
||||||
0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
|
||||||
0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f,
|
|
||||||
0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
|
||||||
0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6c, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
|
|
||||||
0x65, 0x12, 0x21, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e,
|
|
||||||
0x61, 0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71,
|
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d,
|
|
||||||
0x69, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
|
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12,
|
|
||||||
0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x3a,
|
|
||||||
0x01, 0x2a, 0x12, 0x66, 0x0a, 0x0b, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
|
|
||||||
0x65, 0x12, 0x23, 0x2e, 0x67, 0x6d, 0x71, 0x74, 0x74, 0x2e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e,
|
|
||||||
0x61, 0x70, 0x69, 0x2e, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52,
|
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1a,
|
|
||||||
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x6e, 0x73, 0x75,
|
|
||||||
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x3a, 0x01, 0x2a, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b,
|
|
||||||
0x61, 0x64, 0x6d, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_subscription_proto_rawDescOnce sync.Once
|
|
||||||
file_subscription_proto_rawDescData = file_subscription_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_subscription_proto_rawDescGZIP() []byte {
|
|
||||||
file_subscription_proto_rawDescOnce.Do(func() {
|
|
||||||
file_subscription_proto_rawDescData = protoimpl.X.CompressGZIP(file_subscription_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_subscription_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_subscription_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
|
||||||
var file_subscription_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
|
||||||
var file_subscription_proto_goTypes = []interface{}{
|
|
||||||
(SubFilterType)(0), // 0: gmqtt.admin.api.SubFilterType
|
|
||||||
(SubMatchType)(0), // 1: gmqtt.admin.api.SubMatchType
|
|
||||||
(*ListSubscriptionRequest)(nil), // 2: gmqtt.admin.api.ListSubscriptionRequest
|
|
||||||
(*ListSubscriptionResponse)(nil), // 3: gmqtt.admin.api.ListSubscriptionResponse
|
|
||||||
(*FilterSubscriptionRequest)(nil), // 4: gmqtt.admin.api.FilterSubscriptionRequest
|
|
||||||
(*FilterSubscriptionResponse)(nil), // 5: gmqtt.admin.api.FilterSubscriptionResponse
|
|
||||||
(*SubscribeRequest)(nil), // 6: gmqtt.admin.api.SubscribeRequest
|
|
||||||
(*SubscribeResponse)(nil), // 7: gmqtt.admin.api.SubscribeResponse
|
|
||||||
(*UnsubscribeRequest)(nil), // 8: gmqtt.admin.api.UnsubscribeRequest
|
|
||||||
(*Subscription)(nil), // 9: gmqtt.admin.api.Subscription
|
|
||||||
(*empty.Empty)(nil), // 10: google.protobuf.Empty
|
|
||||||
}
|
|
||||||
var file_subscription_proto_depIdxs = []int32{
|
|
||||||
9, // 0: gmqtt.admin.api.ListSubscriptionResponse.subscriptions:type_name -> gmqtt.admin.api.Subscription
|
|
||||||
1, // 1: gmqtt.admin.api.FilterSubscriptionRequest.match_type:type_name -> gmqtt.admin.api.SubMatchType
|
|
||||||
9, // 2: gmqtt.admin.api.FilterSubscriptionResponse.subscriptions:type_name -> gmqtt.admin.api.Subscription
|
|
||||||
9, // 3: gmqtt.admin.api.SubscribeRequest.subscriptions:type_name -> gmqtt.admin.api.Subscription
|
|
||||||
2, // 4: gmqtt.admin.api.SubscriptionService.List:input_type -> gmqtt.admin.api.ListSubscriptionRequest
|
|
||||||
4, // 5: gmqtt.admin.api.SubscriptionService.Filter:input_type -> gmqtt.admin.api.FilterSubscriptionRequest
|
|
||||||
6, // 6: gmqtt.admin.api.SubscriptionService.Subscribe:input_type -> gmqtt.admin.api.SubscribeRequest
|
|
||||||
8, // 7: gmqtt.admin.api.SubscriptionService.Unsubscribe:input_type -> gmqtt.admin.api.UnsubscribeRequest
|
|
||||||
3, // 8: gmqtt.admin.api.SubscriptionService.List:output_type -> gmqtt.admin.api.ListSubscriptionResponse
|
|
||||||
5, // 9: gmqtt.admin.api.SubscriptionService.Filter:output_type -> gmqtt.admin.api.FilterSubscriptionResponse
|
|
||||||
7, // 10: gmqtt.admin.api.SubscriptionService.Subscribe:output_type -> gmqtt.admin.api.SubscribeResponse
|
|
||||||
10, // 11: gmqtt.admin.api.SubscriptionService.Unsubscribe:output_type -> google.protobuf.Empty
|
|
||||||
8, // [8:12] is the sub-list for method output_type
|
|
||||||
4, // [4:8] is the sub-list for method input_type
|
|
||||||
4, // [4:4] is the sub-list for extension type_name
|
|
||||||
4, // [4:4] is the sub-list for extension extendee
|
|
||||||
0, // [0:4] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_subscription_proto_init() }
|
|
||||||
func file_subscription_proto_init() {
|
|
||||||
if File_subscription_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_subscription_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*ListSubscriptionRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_subscription_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*ListSubscriptionResponse); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_subscription_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*FilterSubscriptionRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_subscription_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*FilterSubscriptionResponse); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_subscription_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*SubscribeRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_subscription_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*SubscribeResponse); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_subscription_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*UnsubscribeRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_subscription_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Subscription); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_subscription_proto_rawDesc,
|
|
||||||
NumEnums: 2,
|
|
||||||
NumMessages: 8,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 1,
|
|
||||||
},
|
|
||||||
GoTypes: file_subscription_proto_goTypes,
|
|
||||||
DependencyIndexes: file_subscription_proto_depIdxs,
|
|
||||||
EnumInfos: file_subscription_proto_enumTypes,
|
|
||||||
MessageInfos: file_subscription_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_subscription_proto = out.File
|
|
||||||
file_subscription_proto_rawDesc = nil
|
|
||||||
file_subscription_proto_goTypes = nil
|
|
||||||
file_subscription_proto_depIdxs = nil
|
|
||||||
}
|
|
@ -1,395 +0,0 @@
|
|||||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
|
||||||
// source: subscription.proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package admin is a reverse proxy.
|
|
||||||
|
|
||||||
It translates gRPC into RESTful JSON APIs.
|
|
||||||
*/
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/descriptor"
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Suppress "imported and not used" errors
|
|
||||||
var _ codes.Code
|
|
||||||
var _ io.Reader
|
|
||||||
var _ status.Status
|
|
||||||
var _ = runtime.String
|
|
||||||
var _ = utilities.NewDoubleArray
|
|
||||||
var _ = descriptor.ForMessage
|
|
||||||
|
|
||||||
var (
|
|
||||||
filter_SubscriptionService_List_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
|
||||||
)
|
|
||||||
|
|
||||||
func request_SubscriptionService_List_0(ctx context.Context, marshaler runtime.Marshaler, client SubscriptionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq ListSubscriptionRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
if err := req.ParseForm(); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SubscriptionService_List_0); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.List(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_SubscriptionService_List_0(ctx context.Context, marshaler runtime.Marshaler, server SubscriptionServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq ListSubscriptionRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_SubscriptionService_List_0); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.List(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
filter_SubscriptionService_Filter_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
|
||||||
)
|
|
||||||
|
|
||||||
func request_SubscriptionService_Filter_0(ctx context.Context, marshaler runtime.Marshaler, client SubscriptionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq FilterSubscriptionRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
if err := req.ParseForm(); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SubscriptionService_Filter_0); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.Filter(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_SubscriptionService_Filter_0(ctx context.Context, marshaler runtime.Marshaler, server SubscriptionServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq FilterSubscriptionRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_SubscriptionService_Filter_0); err != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.Filter(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func request_SubscriptionService_Subscribe_0(ctx context.Context, marshaler runtime.Marshaler, client SubscriptionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq SubscribeRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.Subscribe(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_SubscriptionService_Subscribe_0(ctx context.Context, marshaler runtime.Marshaler, server SubscriptionServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq SubscribeRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.Subscribe(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func request_SubscriptionService_Unsubscribe_0(ctx context.Context, marshaler runtime.Marshaler, client SubscriptionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq UnsubscribeRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.Unsubscribe(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_SubscriptionService_Unsubscribe_0(ctx context.Context, marshaler runtime.Marshaler, server SubscriptionServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq UnsubscribeRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.Unsubscribe(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterSubscriptionServiceHandlerServer registers the http handlers for service SubscriptionService to "mux".
|
|
||||||
// UnaryRPC :call SubscriptionServiceServer directly.
|
|
||||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
|
||||||
func RegisterSubscriptionServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server SubscriptionServiceServer) error {
|
|
||||||
|
|
||||||
mux.Handle("GET", pattern_SubscriptionService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_SubscriptionService_List_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_SubscriptionService_List_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("GET", pattern_SubscriptionService_Filter_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_SubscriptionService_Filter_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_SubscriptionService_Filter_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("POST", pattern_SubscriptionService_Subscribe_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_SubscriptionService_Subscribe_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_SubscriptionService_Subscribe_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("POST", pattern_SubscriptionService_Unsubscribe_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_SubscriptionService_Unsubscribe_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_SubscriptionService_Unsubscribe_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterSubscriptionServiceHandlerFromEndpoint is same as RegisterSubscriptionServiceHandler but
|
|
||||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
|
||||||
func RegisterSubscriptionServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
|
||||||
conn, err := grpc.Dial(endpoint, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
if cerr := conn.Close(); cerr != nil {
|
|
||||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
if cerr := conn.Close(); cerr != nil {
|
|
||||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return RegisterSubscriptionServiceHandler(ctx, mux, conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterSubscriptionServiceHandler registers the http handlers for service SubscriptionService to "mux".
|
|
||||||
// The handlers forward requests to the grpc endpoint over "conn".
|
|
||||||
func RegisterSubscriptionServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
|
||||||
return RegisterSubscriptionServiceHandlerClient(ctx, mux, NewSubscriptionServiceClient(conn))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterSubscriptionServiceHandlerClient registers the http handlers for service SubscriptionService
|
|
||||||
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "SubscriptionServiceClient".
|
|
||||||
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "SubscriptionServiceClient"
|
|
||||||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
|
||||||
// "SubscriptionServiceClient" to call the correct interceptors.
|
|
||||||
func RegisterSubscriptionServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client SubscriptionServiceClient) error {
|
|
||||||
|
|
||||||
mux.Handle("GET", pattern_SubscriptionService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_SubscriptionService_List_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_SubscriptionService_List_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("GET", pattern_SubscriptionService_Filter_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_SubscriptionService_Filter_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_SubscriptionService_Filter_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("POST", pattern_SubscriptionService_Subscribe_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_SubscriptionService_Subscribe_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_SubscriptionService_Subscribe_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("POST", pattern_SubscriptionService_Unsubscribe_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_SubscriptionService_Unsubscribe_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_SubscriptionService_Unsubscribe_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
pattern_SubscriptionService_List_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "subscriptions"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
|
|
||||||
pattern_SubscriptionService_Filter_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "filter_subscriptions"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
|
|
||||||
pattern_SubscriptionService_Subscribe_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "subscribe"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
|
|
||||||
pattern_SubscriptionService_Unsubscribe_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "unsubscribe"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
forward_SubscriptionService_List_0 = runtime.ForwardResponseMessage
|
|
||||||
|
|
||||||
forward_SubscriptionService_Filter_0 = runtime.ForwardResponseMessage
|
|
||||||
|
|
||||||
forward_SubscriptionService_Subscribe_0 = runtime.ForwardResponseMessage
|
|
||||||
|
|
||||||
forward_SubscriptionService_Unsubscribe_0 = runtime.ForwardResponseMessage
|
|
||||||
)
|
|
@ -1,215 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
|
||||||
|
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "context"
|
|
||||||
|
|
||||||
empty "github.com/golang/protobuf/ptypes/empty"
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
codes "google.golang.org/grpc/codes"
|
|
||||||
status "google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the grpc package it is being compiled against.
|
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
|
||||||
|
|
||||||
// SubscriptionServiceClient is the client API for SubscriptionService service.
|
|
||||||
//
|
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
|
||||||
type SubscriptionServiceClient interface {
|
|
||||||
// List subscriptions.
|
|
||||||
List(ctx context.Context, in *ListSubscriptionRequest, opts ...grpc.CallOption) (*ListSubscriptionResponse, error)
|
|
||||||
// Filter subscriptions, paging is not supported in this API.
|
|
||||||
Filter(ctx context.Context, in *FilterSubscriptionRequest, opts ...grpc.CallOption) (*FilterSubscriptionResponse, error)
|
|
||||||
// Subscribe topics for the client.
|
|
||||||
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error)
|
|
||||||
// Unsubscribe topics for the client.
|
|
||||||
Unsubscribe(ctx context.Context, in *UnsubscribeRequest, opts ...grpc.CallOption) (*empty.Empty, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type subscriptionServiceClient struct {
|
|
||||||
cc grpc.ClientConnInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSubscriptionServiceClient(cc grpc.ClientConnInterface) SubscriptionServiceClient {
|
|
||||||
return &subscriptionServiceClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *subscriptionServiceClient) List(ctx context.Context, in *ListSubscriptionRequest, opts ...grpc.CallOption) (*ListSubscriptionResponse, error) {
|
|
||||||
out := new(ListSubscriptionResponse)
|
|
||||||
err := c.cc.Invoke(ctx, "/gmqtt.admin.api.SubscriptionService/List", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *subscriptionServiceClient) Filter(ctx context.Context, in *FilterSubscriptionRequest, opts ...grpc.CallOption) (*FilterSubscriptionResponse, error) {
|
|
||||||
out := new(FilterSubscriptionResponse)
|
|
||||||
err := c.cc.Invoke(ctx, "/gmqtt.admin.api.SubscriptionService/Filter", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *subscriptionServiceClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error) {
|
|
||||||
out := new(SubscribeResponse)
|
|
||||||
err := c.cc.Invoke(ctx, "/gmqtt.admin.api.SubscriptionService/Subscribe", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *subscriptionServiceClient) Unsubscribe(ctx context.Context, in *UnsubscribeRequest, opts ...grpc.CallOption) (*empty.Empty, error) {
|
|
||||||
out := new(empty.Empty)
|
|
||||||
err := c.cc.Invoke(ctx, "/gmqtt.admin.api.SubscriptionService/Unsubscribe", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscriptionServiceServer is the server API for SubscriptionService service.
|
|
||||||
// All implementations must embed UnimplementedSubscriptionServiceServer
|
|
||||||
// for forward compatibility
|
|
||||||
type SubscriptionServiceServer interface {
|
|
||||||
// List subscriptions.
|
|
||||||
List(context.Context, *ListSubscriptionRequest) (*ListSubscriptionResponse, error)
|
|
||||||
// Filter subscriptions, paging is not supported in this API.
|
|
||||||
Filter(context.Context, *FilterSubscriptionRequest) (*FilterSubscriptionResponse, error)
|
|
||||||
// Subscribe topics for the client.
|
|
||||||
Subscribe(context.Context, *SubscribeRequest) (*SubscribeResponse, error)
|
|
||||||
// Unsubscribe topics for the client.
|
|
||||||
Unsubscribe(context.Context, *UnsubscribeRequest) (*empty.Empty, error)
|
|
||||||
mustEmbedUnimplementedSubscriptionServiceServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedSubscriptionServiceServer must be embedded to have forward compatible implementations.
|
|
||||||
type UnimplementedSubscriptionServiceServer struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (UnimplementedSubscriptionServiceServer) List(context.Context, *ListSubscriptionRequest) (*ListSubscriptionResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method List not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedSubscriptionServiceServer) Filter(context.Context, *FilterSubscriptionRequest) (*FilterSubscriptionResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Filter not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedSubscriptionServiceServer) Subscribe(context.Context, *SubscribeRequest) (*SubscribeResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Subscribe not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedSubscriptionServiceServer) Unsubscribe(context.Context, *UnsubscribeRequest) (*empty.Empty, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Unsubscribe not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedSubscriptionServiceServer) mustEmbedUnimplementedSubscriptionServiceServer() {}
|
|
||||||
|
|
||||||
// UnsafeSubscriptionServiceServer may be embedded to opt out of forward compatibility for this service.
|
|
||||||
// Use of this interface is not recommended, as added methods to SubscriptionServiceServer will
|
|
||||||
// result in compilation errors.
|
|
||||||
type UnsafeSubscriptionServiceServer interface {
|
|
||||||
mustEmbedUnimplementedSubscriptionServiceServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterSubscriptionServiceServer(s grpc.ServiceRegistrar, srv SubscriptionServiceServer) {
|
|
||||||
s.RegisterService(&_SubscriptionService_serviceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _SubscriptionService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(ListSubscriptionRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(SubscriptionServiceServer).List(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/gmqtt.admin.api.SubscriptionService/List",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(SubscriptionServiceServer).List(ctx, req.(*ListSubscriptionRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _SubscriptionService_Filter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(FilterSubscriptionRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(SubscriptionServiceServer).Filter(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/gmqtt.admin.api.SubscriptionService/Filter",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(SubscriptionServiceServer).Filter(ctx, req.(*FilterSubscriptionRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _SubscriptionService_Subscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(SubscribeRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(SubscriptionServiceServer).Subscribe(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/gmqtt.admin.api.SubscriptionService/Subscribe",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(SubscriptionServiceServer).Subscribe(ctx, req.(*SubscribeRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _SubscriptionService_Unsubscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(UnsubscribeRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(SubscriptionServiceServer).Unsubscribe(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/gmqtt.admin.api.SubscriptionService/Unsubscribe",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(SubscriptionServiceServer).Unsubscribe(ctx, req.(*UnsubscribeRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _SubscriptionService_serviceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "gmqtt.admin.api.SubscriptionService",
|
|
||||||
HandlerType: (*SubscriptionServiceServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "List",
|
|
||||||
Handler: _SubscriptionService_List_Handler,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MethodName: "Filter",
|
|
||||||
Handler: _SubscriptionService_Filter_Handler,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MethodName: "Subscribe",
|
|
||||||
Handler: _SubscriptionService_Subscribe_Handler,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MethodName: "Unsubscribe",
|
|
||||||
Handler: _SubscriptionService_Unsubscribe_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{},
|
|
||||||
Metadata: "subscription.proto",
|
|
||||||
}
|
|
@ -1,390 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
gmqtt "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence/subscription"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSubscriptionService_List(t *testing.T) {
|
|
||||||
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
ss := server.NewMockSubscriptionService(ctrl)
|
|
||||||
client := server.NewMockClient(ctrl)
|
|
||||||
admin := &Admin{
|
|
||||||
store: newStore(nil, mockConfig),
|
|
||||||
}
|
|
||||||
sub := &subscriptionService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
client.EXPECT().ClientOptions().Return(&server.ClientOptions{ClientID: "id"})
|
|
||||||
subscribe := admin.OnSubscribedWrapper(func(ctx context.Context, client server.Client, subscription *gmqtt.Subscription) {})
|
|
||||||
|
|
||||||
subsc := &gmqtt.Subscription{
|
|
||||||
ShareName: "abc",
|
|
||||||
TopicFilter: "t",
|
|
||||||
ID: 1,
|
|
||||||
QoS: 2,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 2,
|
|
||||||
}
|
|
||||||
subscribe(context.Background(), client, subsc)
|
|
||||||
sub.a.store.subscriptionService = ss
|
|
||||||
|
|
||||||
resp, err := sub.List(context.Background(), &ListSubscriptionRequest{
|
|
||||||
PageSize: 0,
|
|
||||||
Page: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
a.Nil(err)
|
|
||||||
a.Len(resp.Subscriptions, 1)
|
|
||||||
rs := resp.Subscriptions[0]
|
|
||||||
a.EqualValues(subsc.QoS, rs.Qos)
|
|
||||||
a.EqualValues(subsc.GetFullTopicName(), rs.TopicName)
|
|
||||||
a.EqualValues(subsc.ID, rs.Id)
|
|
||||||
a.EqualValues(subsc.RetainHandling, rs.RetainHandling)
|
|
||||||
a.EqualValues(subsc.NoLocal, rs.NoLocal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubscriptionService_Filter(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
ss := server.NewMockSubscriptionService(ctrl)
|
|
||||||
admin := &Admin{
|
|
||||||
store: newStore(nil, mockConfig),
|
|
||||||
}
|
|
||||||
sub := &subscriptionService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
sub.a.store.subscriptionService = ss
|
|
||||||
|
|
||||||
ss.EXPECT().Iterate(gomock.Any(), subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeAll,
|
|
||||||
ClientID: "cid",
|
|
||||||
TopicName: "abc",
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := sub.Filter(context.Background(), &FilterSubscriptionRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
FilterType: "1,2,3",
|
|
||||||
MatchType: SubMatchType_SUB_MATCH_TYPE_MATCH_NAME,
|
|
||||||
TopicName: "abc",
|
|
||||||
Limit: 1,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
|
|
||||||
ss.EXPECT().Iterate(gomock.Any(), subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeAll,
|
|
||||||
ClientID: "cid",
|
|
||||||
TopicName: "abc",
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
})
|
|
||||||
|
|
||||||
// test default filter type
|
|
||||||
_, err = sub.Filter(context.Background(), &FilterSubscriptionRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
FilterType: "",
|
|
||||||
MatchType: SubMatchType_SUB_MATCH_TYPE_MATCH_NAME,
|
|
||||||
TopicName: "abc",
|
|
||||||
Limit: 1,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
|
|
||||||
ss.EXPECT().Iterate(gomock.Any(), subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeNonShared | subscription.TypeSYS,
|
|
||||||
ClientID: "cid",
|
|
||||||
TopicName: "abc",
|
|
||||||
MatchType: subscription.MatchName,
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err = sub.Filter(context.Background(), &FilterSubscriptionRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
FilterType: "1,3",
|
|
||||||
MatchType: SubMatchType_SUB_MATCH_TYPE_MATCH_NAME,
|
|
||||||
TopicName: "abc",
|
|
||||||
Limit: 1,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
|
|
||||||
ss.EXPECT().Iterate(gomock.Any(), subscription.IterationOptions{
|
|
||||||
Type: subscription.TypeNonShared | subscription.TypeSYS,
|
|
||||||
ClientID: "cid",
|
|
||||||
TopicName: "abc",
|
|
||||||
MatchType: subscription.MatchFilter,
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err = sub.Filter(context.Background(), &FilterSubscriptionRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
FilterType: "1,3",
|
|
||||||
MatchType: SubMatchType_SUB_MATCH_TYPE_MATCH_FILTER,
|
|
||||||
TopicName: "abc",
|
|
||||||
Limit: 1,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubscriptionService_Filter_InvalidArgument(t *testing.T) {
|
|
||||||
var tt = []struct {
|
|
||||||
name string
|
|
||||||
field string
|
|
||||||
req *FilterSubscriptionRequest
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty_topic_name_with_match_name",
|
|
||||||
field: "match_type",
|
|
||||||
req: &FilterSubscriptionRequest{
|
|
||||||
ClientId: "",
|
|
||||||
FilterType: "",
|
|
||||||
MatchType: SubMatchType_SUB_MATCH_TYPE_MATCH_NAME,
|
|
||||||
TopicName: "",
|
|
||||||
Limit: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty_topic_name_with_match_filter",
|
|
||||||
field: "match_type",
|
|
||||||
req: &FilterSubscriptionRequest{
|
|
||||||
ClientId: "",
|
|
||||||
FilterType: "",
|
|
||||||
MatchType: SubMatchType_SUB_MATCH_TYPE_MATCH_FILTER,
|
|
||||||
TopicName: "",
|
|
||||||
Limit: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid_topic_name",
|
|
||||||
field: "topic_name",
|
|
||||||
req: &FilterSubscriptionRequest{
|
|
||||||
ClientId: "",
|
|
||||||
FilterType: "",
|
|
||||||
MatchType: 0,
|
|
||||||
TopicName: "##",
|
|
||||||
Limit: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
t.Run(v.name, func(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
ss := server.NewMockSubscriptionService(ctrl)
|
|
||||||
admin := &Admin{
|
|
||||||
store: newStore(nil, mockConfig),
|
|
||||||
}
|
|
||||||
sub := &subscriptionService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
sub.a.store.subscriptionService = ss
|
|
||||||
|
|
||||||
_, err := sub.Filter(context.Background(), v.req)
|
|
||||||
a.NotNil(err)
|
|
||||||
a.Contains(err.Error(), v.field)
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubscriptionService_Subscribe(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
ss := server.NewMockSubscriptionService(ctrl)
|
|
||||||
admin := &Admin{
|
|
||||||
store: newStore(nil, mockConfig),
|
|
||||||
}
|
|
||||||
sub := &subscriptionService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
sub.a.store.subscriptionService = ss
|
|
||||||
|
|
||||||
subs := []*Subscription{
|
|
||||||
{
|
|
||||||
TopicName: "$share/a/b",
|
|
||||||
Id: 1,
|
|
||||||
Qos: 2,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 2,
|
|
||||||
}, {
|
|
||||||
TopicName: "abc",
|
|
||||||
Id: 1,
|
|
||||||
Qos: 2,
|
|
||||||
NoLocal: true,
|
|
||||||
RetainAsPublished: true,
|
|
||||||
RetainHandling: 2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
var expectedSubs []*gmqtt.Subscription
|
|
||||||
for _, v := range subs {
|
|
||||||
shareName, filter := subscription.SplitTopic(v.TopicName)
|
|
||||||
s := &gmqtt.Subscription{
|
|
||||||
ShareName: shareName,
|
|
||||||
TopicFilter: filter,
|
|
||||||
ID: v.Id,
|
|
||||||
QoS: byte(v.Qos),
|
|
||||||
NoLocal: v.NoLocal,
|
|
||||||
RetainAsPublished: v.RetainAsPublished,
|
|
||||||
RetainHandling: byte(v.RetainHandling),
|
|
||||||
}
|
|
||||||
expectedSubs = append(expectedSubs, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
ss.EXPECT().Subscribe("cid", expectedSubs).Return(subscription.SubscribeResult{
|
|
||||||
{
|
|
||||||
AlreadyExisted: true,
|
|
||||||
}, {
|
|
||||||
AlreadyExisted: false,
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
resp, err := sub.Subscribe(context.Background(), &SubscribeRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
Subscriptions: subs,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
resp.New = []bool{false, true}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubscriptionService_Subscribe_InvalidArgument(t *testing.T) {
|
|
||||||
var tt = []struct {
|
|
||||||
name string
|
|
||||||
req *SubscribeRequest
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty_client_id",
|
|
||||||
req: &SubscribeRequest{
|
|
||||||
ClientId: "",
|
|
||||||
Subscriptions: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty_subscriptions",
|
|
||||||
req: &SubscribeRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid_subscriptions",
|
|
||||||
req: &SubscribeRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
Subscriptions: []*Subscription{
|
|
||||||
{
|
|
||||||
TopicName: "##",
|
|
||||||
Id: 0,
|
|
||||||
Qos: 0,
|
|
||||||
NoLocal: false,
|
|
||||||
RetainAsPublished: false,
|
|
||||||
RetainHandling: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
t.Run(v.name, func(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
ss := server.NewMockSubscriptionService(ctrl)
|
|
||||||
admin := &Admin{
|
|
||||||
store: newStore(nil, mockConfig),
|
|
||||||
}
|
|
||||||
sub := &subscriptionService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
sub.a.store.subscriptionService = ss
|
|
||||||
|
|
||||||
_, err := sub.Subscribe(context.Background(), v.req)
|
|
||||||
a.NotNil(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubscriptionService_Unsubscribe(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
ss := server.NewMockSubscriptionService(ctrl)
|
|
||||||
admin := &Admin{
|
|
||||||
store: newStore(nil, mockConfig),
|
|
||||||
}
|
|
||||||
sub := &subscriptionService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
sub.a.store.subscriptionService = ss
|
|
||||||
|
|
||||||
topics := []string{
|
|
||||||
"a", "b",
|
|
||||||
}
|
|
||||||
ss.EXPECT().Unsubscribe("cid", topics)
|
|
||||||
_, err := sub.Unsubscribe(context.Background(), &UnsubscribeRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
Topics: topics,
|
|
||||||
})
|
|
||||||
a.Nil(err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubscriptionService_Unsubscribe_InvalidArgument(t *testing.T) {
|
|
||||||
var tt = []struct {
|
|
||||||
name string
|
|
||||||
req *UnsubscribeRequest
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty_client_id",
|
|
||||||
req: &UnsubscribeRequest{
|
|
||||||
ClientId: "",
|
|
||||||
Topics: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid_topic_name",
|
|
||||||
req: &UnsubscribeRequest{
|
|
||||||
ClientId: "cid",
|
|
||||||
Topics: []string{"+", "##"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, v := range tt {
|
|
||||||
t.Run(v.name, func(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
ss := server.NewMockSubscriptionService(ctrl)
|
|
||||||
admin := &Admin{
|
|
||||||
store: newStore(nil, mockConfig),
|
|
||||||
}
|
|
||||||
sub := &subscriptionService{
|
|
||||||
a: admin,
|
|
||||||
}
|
|
||||||
sub.a.store.subscriptionService = ss
|
|
||||||
|
|
||||||
_, err := sub.Unsubscribe(context.Background(), v.req)
|
|
||||||
a.NotNil(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,260 +0,0 @@
|
|||||||
{
|
|
||||||
"swagger": "2.0",
|
|
||||||
"info": {
|
|
||||||
"title": "client.proto",
|
|
||||||
"version": "version not set"
|
|
||||||
},
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"paths": {
|
|
||||||
"/v1/clients": {
|
|
||||||
"get": {
|
|
||||||
"summary": "List clients",
|
|
||||||
"operationId": "List",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/apiListClientResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"description": "An unexpected error response",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/runtimeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "page_size",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "page",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"ClientService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/v1/clients/{client_id}": {
|
|
||||||
"get": {
|
|
||||||
"summary": "Get the client for given client id.\nReturn NotFound error when client not found.",
|
|
||||||
"operationId": "Get",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/apiGetClientResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"description": "An unexpected error response",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/runtimeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "client_id",
|
|
||||||
"in": "path",
|
|
||||||
"required": true,
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"ClientService"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"delete": {
|
|
||||||
"summary": "Disconnect the client for given client id.",
|
|
||||||
"operationId": "Delete",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"description": "An unexpected error response",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/runtimeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "client_id",
|
|
||||||
"in": "path",
|
|
||||||
"required": true,
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "clean_session",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "boolean",
|
|
||||||
"format": "boolean"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"ClientService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"definitions": {
|
|
||||||
"apiClient": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"client_id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"keep_alive": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"remote_addr": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"local_addr": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"connected_at": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "date-time"
|
|
||||||
},
|
|
||||||
"disconnected_at": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "date-time"
|
|
||||||
},
|
|
||||||
"session_expiry": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"max_inflight": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"inflight_len": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"max_queue": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"queue_len": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"subscriptions_current": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"subscriptions_total": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"packets_received_bytes": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "uint64"
|
|
||||||
},
|
|
||||||
"packets_received_nums": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "uint64"
|
|
||||||
},
|
|
||||||
"packets_send_bytes": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "uint64"
|
|
||||||
},
|
|
||||||
"packets_send_nums": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "uint64"
|
|
||||||
},
|
|
||||||
"message_dropped": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "uint64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apiGetClientResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"client": {
|
|
||||||
"$ref": "#/definitions/apiClient"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apiListClientResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"clients": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/apiClient"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"total_count": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"protobufAny": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type_url": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "byte"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"runtimeError": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"error": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"code": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"details": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/protobufAny"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
{
|
|
||||||
"swagger": "2.0",
|
|
||||||
"info": {
|
|
||||||
"title": "publish.proto",
|
|
||||||
"version": "version not set"
|
|
||||||
},
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"paths": {
|
|
||||||
"/v1/publish": {
|
|
||||||
"post": {
|
|
||||||
"summary": "Publish message to broker",
|
|
||||||
"operationId": "Publish",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"description": "An unexpected error response",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/runtimeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/apiPublishRequest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"PublishService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"definitions": {
|
|
||||||
"apiPublishRequest": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"topic_name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"payload": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"qos": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"retained": {
|
|
||||||
"type": "boolean",
|
|
||||||
"format": "boolean"
|
|
||||||
},
|
|
||||||
"content_type": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "the following fields are using in v5 client."
|
|
||||||
},
|
|
||||||
"correlation_data": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"message_expiry": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"payload_format": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"response_topic": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"user_properties": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/apiUserProperties"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apiUserProperties": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"K": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "byte"
|
|
||||||
},
|
|
||||||
"V": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "byte"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"protobufAny": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type_url": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "byte"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"runtimeError": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"error": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"code": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"details": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/protobufAny"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,329 +0,0 @@
|
|||||||
{
|
|
||||||
"swagger": "2.0",
|
|
||||||
"info": {
|
|
||||||
"title": "subscription.proto",
|
|
||||||
"version": "version not set"
|
|
||||||
},
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"paths": {
|
|
||||||
"/v1/filter_subscriptions": {
|
|
||||||
"get": {
|
|
||||||
"summary": "Filter subscriptions, paging is not supported in this API.",
|
|
||||||
"operationId": "Filter",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/apiFilterSubscriptionResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"description": "An unexpected error response",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/runtimeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "client_id",
|
|
||||||
"description": "If set, only filter the subscriptions that belongs to the client.",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "filter_type",
|
|
||||||
"description": "filter_type indicates what kinds of topics are going to filter.\nIf there are multiple types, use ',' to separate. e.g : 1,2\nThere are 3 kinds of topic can be filtered, defined by SubFilterType:\n1 = System Topic(begin with '$')\n2 = Shared Topic\n3 = NonShared Topic.",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "match_type",
|
|
||||||
"description": "If 1 (SUB_MATCH_TYPE_MATCH_NAME), the server will return subscriptions which has the same topic name with request topic_name.\nIf 2 (SUB_MATCH_TYPE_MATCH_FILTER),the server will return subscriptions which match the request topic_name .\nmatch_type must be set when filter_type is not empty.",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"SUB_MATCH_TYPE_MATCH_UNSPECIFIED",
|
|
||||||
"SUB_MATCH_TYPE_MATCH_NAME",
|
|
||||||
"SUB_MATCH_TYPE_MATCH_FILTER"
|
|
||||||
],
|
|
||||||
"default": "SUB_MATCH_TYPE_MATCH_UNSPECIFIED"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "topic_name",
|
|
||||||
"description": "topic_name must be set when match_type is not zero.",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "limit",
|
|
||||||
"description": "The maximum subscriptions can be returned.",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"SubscriptionService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/v1/subscribe": {
|
|
||||||
"post": {
|
|
||||||
"summary": "Subscribe topics for the client.",
|
|
||||||
"operationId": "Subscribe",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/apiSubscribeResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"description": "An unexpected error response",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/runtimeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/apiSubscribeRequest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"SubscriptionService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/v1/subscriptions": {
|
|
||||||
"get": {
|
|
||||||
"summary": "List subscriptions.",
|
|
||||||
"operationId": "List",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/apiListSubscriptionResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"description": "An unexpected error response",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/runtimeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "page_size",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "page",
|
|
||||||
"in": "query",
|
|
||||||
"required": false,
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"SubscriptionService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/v1/unsubscribe": {
|
|
||||||
"post": {
|
|
||||||
"summary": "Unsubscribe topics for the client.",
|
|
||||||
"operationId": "Unsubscribe",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"description": "An unexpected error response",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/runtimeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/apiUnsubscribeRequest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"SubscriptionService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"definitions": {
|
|
||||||
"apiFilterSubscriptionResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"subscriptions": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/apiSubscription"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apiListSubscriptionResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"subscriptions": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/apiSubscription"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"total_count": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apiSubMatchType": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"SUB_MATCH_TYPE_MATCH_UNSPECIFIED",
|
|
||||||
"SUB_MATCH_TYPE_MATCH_NAME",
|
|
||||||
"SUB_MATCH_TYPE_MATCH_FILTER"
|
|
||||||
],
|
|
||||||
"default": "SUB_MATCH_TYPE_MATCH_UNSPECIFIED"
|
|
||||||
},
|
|
||||||
"apiSubscribeRequest": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"client_id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subscriptions": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/apiSubscription"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apiSubscribeResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"new": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "boolean",
|
|
||||||
"format": "boolean"
|
|
||||||
},
|
|
||||||
"description": "indicates whether it is a new subscription or the subscription is already existed."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apiSubscription": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"topic_name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"qos": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"no_local": {
|
|
||||||
"type": "boolean",
|
|
||||||
"format": "boolean"
|
|
||||||
},
|
|
||||||
"retain_as_published": {
|
|
||||||
"type": "boolean",
|
|
||||||
"format": "boolean"
|
|
||||||
},
|
|
||||||
"retain_handling": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"client_id": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apiUnsubscribeRequest": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"client_id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"topics": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"protobufAny": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type_url": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "byte"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"runtimeError": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"error": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"code": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"details": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/protobufAny"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,109 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrNotFound represents a not found error.
|
|
||||||
var ErrNotFound = status.Error(codes.NotFound, "not found")
|
|
||||||
|
|
||||||
// Indexer provides a index for a ordered list that supports queries in O(1).
|
|
||||||
// All methods are not concurrency-safe.
|
|
||||||
type Indexer struct {
|
|
||||||
index map[string]*list.Element
|
|
||||||
rows *list.List
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIndexer is the constructor of Indexer.
|
|
||||||
func NewIndexer() *Indexer {
|
|
||||||
return &Indexer{
|
|
||||||
index: make(map[string]*list.Element),
|
|
||||||
rows: list.New(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the value for the id.
|
|
||||||
func (i *Indexer) Set(id string, value interface{}) {
|
|
||||||
if e, ok := i.index[id]; ok {
|
|
||||||
e.Value = value
|
|
||||||
} else {
|
|
||||||
elem := i.rows.PushBack(value)
|
|
||||||
i.index[id] = elem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes and returns the value for the given id.
|
|
||||||
// Return nil if not found.
|
|
||||||
func (i *Indexer) Remove(id string) *list.Element {
|
|
||||||
elem := i.index[id]
|
|
||||||
if elem != nil {
|
|
||||||
i.rows.Remove(elem)
|
|
||||||
}
|
|
||||||
delete(i.index, id)
|
|
||||||
return elem
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByID returns the value for the given id.
|
|
||||||
// Return nil if not found.
|
|
||||||
// Notice: Any access to the return *list.Element also require the mutex,
|
|
||||||
// because the Set method can modify the Value for *list.Element when updating the Value for the same id.
|
|
||||||
// If the caller needs the Value in *list.Element, it must get the Value before the next Set is called.
|
|
||||||
func (i *Indexer) GetByID(id string) *list.Element {
|
|
||||||
return i.index[id]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate iterates at most n elements in the list begin from offset.
|
|
||||||
// Notice: Any access to the *list.Element in fn also require the mutex,
|
|
||||||
// because the Set method can modify the Value for *list.Element when updating the Value for the same id.
|
|
||||||
// If the caller needs the Value in *list.Element, it must get the Value before the next Set is called.
|
|
||||||
func (i *Indexer) Iterate(fn func(elem *list.Element), offset, n uint) {
|
|
||||||
if i.rows.Len() < int(offset) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var j uint
|
|
||||||
for e := i.rows.Front(); e != nil; e = e.Next() {
|
|
||||||
if j >= offset && j < offset+n {
|
|
||||||
fn(e)
|
|
||||||
}
|
|
||||||
if j == offset+n {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the length of list.
|
|
||||||
func (i *Indexer) Len() int {
|
|
||||||
return i.rows.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPage gets page and pageSize from request params.
|
|
||||||
func GetPage(reqPage, reqPageSize uint32) (page, pageSize uint) {
|
|
||||||
page = 1
|
|
||||||
pageSize = 20
|
|
||||||
if reqPage != 0 {
|
|
||||||
page = uint(reqPage)
|
|
||||||
}
|
|
||||||
if reqPageSize != 0 {
|
|
||||||
pageSize = uint(reqPageSize)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetOffsetN(page, pageSize uint) (offset, n uint) {
|
|
||||||
offset = (page - 1) * pageSize
|
|
||||||
n = pageSize
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrInvalidArgument is a wrapper function for easier invalid argument error handling.
|
|
||||||
func ErrInvalidArgument(name string, msg string) error {
|
|
||||||
errString := "invalid " + name
|
|
||||||
if msg != "" {
|
|
||||||
errString = errString + ":" + msg
|
|
||||||
}
|
|
||||||
return status.Error(codes.InvalidArgument, errString)
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIndexer(t *testing.T) {
|
|
||||||
a := assert.New(t)
|
|
||||||
i := NewIndexer()
|
|
||||||
for j := 0; j < 100; j++ {
|
|
||||||
i.Set(strconv.Itoa(j), j)
|
|
||||||
a.EqualValues(j, i.GetByID(strconv.Itoa(j)).Value)
|
|
||||||
}
|
|
||||||
a.EqualValues(100, i.Len())
|
|
||||||
|
|
||||||
var jj int
|
|
||||||
i.Iterate(func(elem *list.Element) {
|
|
||||||
v := elem.Value.(int)
|
|
||||||
a.Equal(jj, v)
|
|
||||||
jj++
|
|
||||||
}, 0, uint(i.Len()))
|
|
||||||
|
|
||||||
e := i.Remove("5")
|
|
||||||
a.Equal(5, e.Value.(int))
|
|
||||||
|
|
||||||
var rs []int
|
|
||||||
i.Iterate(func(elem *list.Element) {
|
|
||||||
rs = append(rs, elem.Value.(int))
|
|
||||||
}, 4, 2)
|
|
||||||
// 5 is removed
|
|
||||||
a.Equal([]int{4, 6}, rs)
|
|
||||||
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
package aplugin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/aplugin/snowflake"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ server.Plugin = (*APlugin)(nil)
|
|
||||||
|
|
||||||
const Name = "aplugin"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
server.RegisterPlugin(Name, New)
|
|
||||||
config.RegisterDefaultPluginConfig(Name, &DefaultConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(config config.Config) (server.Plugin, error) {
|
|
||||||
return newAPlugin()
|
|
||||||
}
|
|
||||||
|
|
||||||
type APlugin struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
wg *sync.WaitGroup
|
|
||||||
node *snowflake.Node
|
|
||||||
log *zap.SugaredLogger
|
|
||||||
ackMap sync.Map // async ack
|
|
||||||
driverClients map[string]*DriverClient // driver map, key is username
|
|
||||||
publisher server.Publisher
|
|
||||||
publishChan chan PublishInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAPlugin() (*APlugin, error) {
|
|
||||||
node, err := snowflake.NewNode(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
return &APlugin{
|
|
||||||
node: node,
|
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
wg: &sync.WaitGroup{},
|
|
||||||
log: zap.NewNop().Sugar(),
|
|
||||||
driverClients: make(map[string]*DriverClient),
|
|
||||||
publishChan: make(chan PublishInfo, 32),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) Load(service server.Server) error {
|
|
||||||
t.log = server.LoggerWithField(zap.String("plugin", Name)).Sugar()
|
|
||||||
t.publisher = service.Publisher()
|
|
||||||
t.wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer t.wg.Done()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-t.ctx.Done():
|
|
||||||
t.log.Infof("plugin(%s) exit", t.Name())
|
|
||||||
return
|
|
||||||
case msg := <-t.publishChan:
|
|
||||||
t.log.Infof("publish msg: topic: %s, payload: %s", msg.Topic, string(msg.Payload))
|
|
||||||
t.publisher.Publish(&mqttbroker.Message{
|
|
||||||
QoS: 1,
|
|
||||||
Topic: msg.Topic,
|
|
||||||
Payload: msg.Payload,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) Unload() error {
|
|
||||||
t.cancel()
|
|
||||||
t.wg.Wait()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) Name() string {
|
|
||||||
return Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) genAckChan(id int64) *MsgAckChan {
|
|
||||||
ack := &MsgAckChan{
|
|
||||||
Id: id,
|
|
||||||
DataChan: make(chan interface{}, 1),
|
|
||||||
}
|
|
||||||
t.ackMap.Store(id, ack)
|
|
||||||
return ack
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) publishWithAckMsg(id int64, topic string, tp int, msg interface{}) (*MsgAckChan, error) {
|
|
||||||
payload, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
buff, err := json.Marshal(AsyncMsg{
|
|
||||||
Id: id,
|
|
||||||
Type: tp,
|
|
||||||
Data: payload,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ackChan := t.genAckChan(id)
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
t.ackMap.Delete(id)
|
|
||||||
return nil, errors.New("send auth msg to publish chan timeout")
|
|
||||||
case t.publishChan <- PublishInfo{
|
|
||||||
Topic: topic,
|
|
||||||
Payload: buff,
|
|
||||||
}:
|
|
||||||
return ackChan, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) publishNotifyMsg(id int64, topic string, tp int, msg interface{}) error {
|
|
||||||
payload, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buff, err := json.Marshal(AsyncMsg{
|
|
||||||
Id: id,
|
|
||||||
Type: tp,
|
|
||||||
Data: payload,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
return errors.New("send auth msg to publish chan timeout")
|
|
||||||
case t.publishChan <- PublishInfo{
|
|
||||||
Topic: topic,
|
|
||||||
Payload: buff,
|
|
||||||
}:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) validate(username, password string) error {
|
|
||||||
//t.log.Debugf("got clientId: %s, username: %s, password: %s", clientId, username, password)
|
|
||||||
passwd, err := md5GenPasswd(username)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if passwd[8:24] != password {
|
|
||||||
return errors.New("auth failure")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func md5GenPasswd(username string) (string, error) {
|
|
||||||
h := md5.New()
|
|
||||||
_, err := h.Write([]byte(username))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
rs := h.Sum(nil)
|
|
||||||
return hex.EncodeToString(rs), nil
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package aplugin
|
|
||||||
|
|
||||||
// Config is the configuration for the aplugin plugin.
|
|
||||||
type Config struct {
|
|
||||||
// add your config fields
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the configuration, and return an error if it is invalid.
|
|
||||||
func (c *Config) Validate() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultConfig is the default configuration.
|
|
||||||
var DefaultConfig = Config{}
|
|
||||||
|
|
||||||
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,192 +0,0 @@
|
|||||||
package aplugin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Auth = iota + 1 // 连接鉴权
|
|
||||||
Sub // 设备订阅校验
|
|
||||||
Pub // 设备发布校验
|
|
||||||
UnSub
|
|
||||||
Connected
|
|
||||||
Closed
|
|
||||||
)
|
|
||||||
|
|
||||||
type PublishInfo struct {
|
|
||||||
Topic string
|
|
||||||
Payload []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type MsgAckChan struct {
|
|
||||||
Mu sync.Mutex
|
|
||||||
Id int64
|
|
||||||
IsClosed bool
|
|
||||||
DataChan chan interface{} // auth ack, device sub ack, device pub ack
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mac *MsgAckChan) tryCloseChan() {
|
|
||||||
mac.Mu.Lock()
|
|
||||||
defer mac.Mu.Unlock()
|
|
||||||
if !mac.IsClosed {
|
|
||||||
close(mac.DataChan)
|
|
||||||
mac.IsClosed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mac *MsgAckChan) trySendDataAndCloseChan(data interface{}) bool {
|
|
||||||
mac.Mu.Lock()
|
|
||||||
defer mac.Mu.Unlock()
|
|
||||||
if !mac.IsClosed {
|
|
||||||
mac.DataChan <- data
|
|
||||||
close(mac.DataChan)
|
|
||||||
mac.IsClosed = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type (
|
|
||||||
// AsyncMsg 异步消息统一收发
|
|
||||||
AsyncMsg struct {
|
|
||||||
Id int64
|
|
||||||
Type int // 1:连接鉴权,2:设备订阅校验,3:设备发布校验,4:unsub,6:closed
|
|
||||||
Data json.RawMessage // auth ack sub ack pub ack
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
AuthCheck struct {
|
|
||||||
ClientId string
|
|
||||||
Username string
|
|
||||||
Password string
|
|
||||||
Pass bool
|
|
||||||
Msg string
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type PubTopic struct {
|
|
||||||
ClientId string
|
|
||||||
Username string
|
|
||||||
Topic string
|
|
||||||
QoS byte
|
|
||||||
Retained bool
|
|
||||||
Pass bool
|
|
||||||
Msg string
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubTopic 三方设备或服务订阅topic校验
|
|
||||||
type (
|
|
||||||
SubTopic struct {
|
|
||||||
Topic string
|
|
||||||
QoS byte
|
|
||||||
Pass bool
|
|
||||||
Msg string
|
|
||||||
}
|
|
||||||
SubTopics struct {
|
|
||||||
ClientId string
|
|
||||||
Username string
|
|
||||||
Topics []SubTopic
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// ConnectedNotify 三方设备或服务连接成功后通知对应驱动
|
|
||||||
ConnectedNotify struct {
|
|
||||||
ClientId string
|
|
||||||
Username string
|
|
||||||
IP string
|
|
||||||
Port string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClosedNotify 三方设备或服务断开连接后通知对应驱动
|
|
||||||
ClosedNotify struct {
|
|
||||||
ClientId string
|
|
||||||
Username string
|
|
||||||
}
|
|
||||||
|
|
||||||
UnSubNotify struct {
|
|
||||||
ClientId string
|
|
||||||
Username string
|
|
||||||
Topics []string
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type DriverClient struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
ClientId string
|
|
||||||
Username string
|
|
||||||
PubTopic string
|
|
||||||
SubTopic string
|
|
||||||
ClientMap map[string]*ThirdClient // key is clientId
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *DriverClient) AddThirdClient(client server.Client) {
|
|
||||||
dc.mu.Lock()
|
|
||||||
defer dc.mu.Unlock()
|
|
||||||
dc.ClientMap[client.ClientOptions().ClientID] = newThirdClient(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *DriverClient) DeleteThirdClient(clientId string) {
|
|
||||||
dc.mu.Lock()
|
|
||||||
defer dc.mu.Unlock()
|
|
||||||
delete(dc.ClientMap, clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ThirdClient struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
client server.Client
|
|
||||||
subs map[string]struct{}
|
|
||||||
pubs map[string]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *ThirdClient) AddTopics(topics []string, t int) {
|
|
||||||
tc.mu.Lock()
|
|
||||||
defer tc.mu.Unlock()
|
|
||||||
|
|
||||||
if t == Sub {
|
|
||||||
for i := range topics {
|
|
||||||
tc.subs[topics[i]] = struct{}{}
|
|
||||||
}
|
|
||||||
} else if t == Pub {
|
|
||||||
for i := range topics {
|
|
||||||
tc.pubs[topics[i]] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *ThirdClient) DeleteTopics(topics []string, t int) {
|
|
||||||
tc.mu.Lock()
|
|
||||||
defer tc.mu.Unlock()
|
|
||||||
|
|
||||||
if t == UnSub {
|
|
||||||
for i := range topics {
|
|
||||||
delete(tc.subs, topics[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *ThirdClient) CheckTopic(topic string, t int) bool {
|
|
||||||
tc.mu.RLock()
|
|
||||||
defer tc.mu.RUnlock()
|
|
||||||
|
|
||||||
if t == Sub {
|
|
||||||
_, ok := tc.subs[topic]
|
|
||||||
return ok
|
|
||||||
} else if t == Pub {
|
|
||||||
_, ok := tc.pubs[topic]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func newThirdClient(c server.Client) *ThirdClient {
|
|
||||||
return &ThirdClient{
|
|
||||||
client: c,
|
|
||||||
subs: make(map[string]struct{}),
|
|
||||||
pubs: make(map[string]struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
package aplugin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t *APlugin) HookWrapper() server.HookWrapper {
|
|
||||||
return server.HookWrapper{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) OnBasicAuthWrapper(pre server.OnBasicAuth) server.OnBasicAuth {
|
|
||||||
return func(ctx context.Context, client server.Client, req *server.ConnectRequest) (err error) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) OnSubscribeWrapper(pre server.OnSubscribe) server.OnSubscribe {
|
|
||||||
return func(ctx context.Context, client server.Client, req *server.SubscribeRequest) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) OnUnsubscribeWrapper(pre server.OnUnsubscribe) server.OnUnsubscribe {
|
|
||||||
return func(ctx context.Context, client server.Client, req *server.UnsubscribeRequest) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) OnMsgArrivedWrapper(pre server.OnMsgArrived) server.OnMsgArrived {
|
|
||||||
return func(ctx context.Context, client server.Client, req *server.MsgArrivedRequest) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) OnConnectedWrapper(pre server.OnConnected) server.OnConnected {
|
|
||||||
return func(ctx context.Context, client server.Client) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *APlugin) OnClosedWrapper(pre server.OnClosed) server.OnClosed {
|
|
||||||
return func(ctx context.Context, client server.Client, err error) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,391 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2016, Bruce
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Package snowflake provides a very simple Twitter snowflake generator and parser.
|
|
||||||
package snowflake
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Epoch is set to the twitter snowflake epoch of Nov 04 2010 01:42:54 UTC in milliseconds
|
|
||||||
// You may customize this to set a different epoch for your application.
|
|
||||||
Epoch int64 = 1288834974657
|
|
||||||
|
|
||||||
// NodeBits holds the number of bits to use for Node
|
|
||||||
// Remember, you have a total 22 bits to share between Node/Step
|
|
||||||
NodeBits uint8 = 10
|
|
||||||
|
|
||||||
// StepBits holds the number of bits to use for Step
|
|
||||||
// Remember, you have a total 22 bits to share between Node/Step
|
|
||||||
StepBits uint8 = 12
|
|
||||||
|
|
||||||
// DEPRECATED: the below four variables will be removed in a future release.
|
|
||||||
mu sync.Mutex
|
|
||||||
nodeMax int64 = -1 ^ (-1 << NodeBits)
|
|
||||||
nodeMask = nodeMax << StepBits
|
|
||||||
stepMask int64 = -1 ^ (-1 << StepBits)
|
|
||||||
timeShift = NodeBits + StepBits
|
|
||||||
nodeShift = StepBits
|
|
||||||
)
|
|
||||||
|
|
||||||
const encodeBase32Map = "ybndrfg8ejkmcpqxot1uwisza345h769"
|
|
||||||
|
|
||||||
var decodeBase32Map [256]byte
|
|
||||||
|
|
||||||
const encodeBase58Map = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
|
||||||
|
|
||||||
var decodeBase58Map [256]byte
|
|
||||||
|
|
||||||
// A JSONSyntaxError is returned from UnmarshalJSON if an invalid ID is provided.
|
|
||||||
type JSONSyntaxError struct{ original []byte }
|
|
||||||
|
|
||||||
func (j JSONSyntaxError) Error() string {
|
|
||||||
return fmt.Sprintf("invalid snowflake ID %q", string(j.original))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrInvalidBase58 is returned by ParseBase58 when given an invalid []byte
|
|
||||||
var ErrInvalidBase58 = errors.New("invalid base58")
|
|
||||||
|
|
||||||
// ErrInvalidBase32 is returned by ParseBase32 when given an invalid []byte
|
|
||||||
var ErrInvalidBase32 = errors.New("invalid base32")
|
|
||||||
|
|
||||||
// Create maps for decoding Base58/Base32.
|
|
||||||
// This speeds up the process tremendously.
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
for i := 0; i < len(decodeBase58Map); i++ {
|
|
||||||
decodeBase58Map[i] = 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(encodeBase58Map); i++ {
|
|
||||||
decodeBase58Map[encodeBase58Map[i]] = byte(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(decodeBase32Map); i++ {
|
|
||||||
decodeBase32Map[i] = 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(encodeBase32Map); i++ {
|
|
||||||
decodeBase32Map[encodeBase32Map[i]] = byte(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Node struct holds the basic information needed for a snowflake generator
|
|
||||||
// node
|
|
||||||
type Node struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
epoch time.Time
|
|
||||||
time int64
|
|
||||||
node int64
|
|
||||||
step int64
|
|
||||||
|
|
||||||
nodeMax int64
|
|
||||||
nodeMask int64
|
|
||||||
stepMask int64
|
|
||||||
timeShift uint8
|
|
||||||
nodeShift uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// An ID is a custom type used for a snowflake ID. This is used so we can
|
|
||||||
// attach methods onto the ID.
|
|
||||||
type ID int64
|
|
||||||
|
|
||||||
// NewNode returns a new snowflake node that can be used to generate snowflake
|
|
||||||
// IDs
|
|
||||||
func NewNode(node int64) (*Node, error) {
|
|
||||||
|
|
||||||
// re-calc in case custom NodeBits or StepBits were set
|
|
||||||
// DEPRECATED: the below block will be removed in a future release.
|
|
||||||
mu.Lock()
|
|
||||||
nodeMax = -1 ^ (-1 << NodeBits)
|
|
||||||
nodeMask = nodeMax << StepBits
|
|
||||||
stepMask = -1 ^ (-1 << StepBits)
|
|
||||||
timeShift = NodeBits + StepBits
|
|
||||||
nodeShift = StepBits
|
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
n := Node{}
|
|
||||||
n.node = node
|
|
||||||
n.nodeMax = -1 ^ (-1 << NodeBits)
|
|
||||||
n.nodeMask = n.nodeMax << StepBits
|
|
||||||
n.stepMask = -1 ^ (-1 << StepBits)
|
|
||||||
n.timeShift = NodeBits + StepBits
|
|
||||||
n.nodeShift = StepBits
|
|
||||||
|
|
||||||
if n.node < 0 || n.node > n.nodeMax {
|
|
||||||
return nil, errors.New("Node number must be between 0 and " + strconv.FormatInt(n.nodeMax, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
var curTime = time.Now()
|
|
||||||
// add time.Duration to curTime to make sure we use the monotonic clock if available
|
|
||||||
n.epoch = curTime.Add(time.Unix(Epoch/1000, (Epoch%1000)*1000000).Sub(curTime))
|
|
||||||
|
|
||||||
return &n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate creates and returns a unique snowflake ID
|
|
||||||
// To help guarantee uniqueness
|
|
||||||
// - Make sure your system is keeping accurate system time
|
|
||||||
// - Make sure you never have multiple nodes running with the same node ID
|
|
||||||
func (n *Node) Generate() ID {
|
|
||||||
|
|
||||||
n.mu.Lock()
|
|
||||||
|
|
||||||
now := time.Since(n.epoch).Nanoseconds() / 1000000
|
|
||||||
|
|
||||||
if now == n.time {
|
|
||||||
n.step = (n.step + 1) & n.stepMask
|
|
||||||
|
|
||||||
if n.step == 0 {
|
|
||||||
for now <= n.time {
|
|
||||||
now = time.Since(n.epoch).Nanoseconds() / 1000000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n.step = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
n.time = now
|
|
||||||
|
|
||||||
r := ID((now)<<n.timeShift |
|
|
||||||
(n.node << n.nodeShift) |
|
|
||||||
(n.step),
|
|
||||||
)
|
|
||||||
|
|
||||||
n.mu.Unlock()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 returns an int64 of the snowflake ID
|
|
||||||
func (f ID) Int64() int64 {
|
|
||||||
return int64(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseInt64 converts an int64 into a snowflake ID
|
|
||||||
func ParseInt64(id int64) ID {
|
|
||||||
return ID(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string of the snowflake ID
|
|
||||||
func (f ID) String() string {
|
|
||||||
return strconv.FormatInt(int64(f), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseString converts a string into a snowflake ID
|
|
||||||
func ParseString(id string) (ID, error) {
|
|
||||||
i, err := strconv.ParseInt(id, 10, 64)
|
|
||||||
return ID(i), err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base2 returns a string base2 of the snowflake ID
|
|
||||||
func (f ID) Base2() string {
|
|
||||||
return strconv.FormatInt(int64(f), 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBase2 converts a Base2 string into a snowflake ID
|
|
||||||
func ParseBase2(id string) (ID, error) {
|
|
||||||
i, err := strconv.ParseInt(id, 2, 64)
|
|
||||||
return ID(i), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base32 uses the z-base-32 character set but encodes and decodes similar
|
|
||||||
// to base58, allowing it to create an even smaller result string.
|
|
||||||
// NOTE: There are many different base32 implementations so becareful when
|
|
||||||
// doing any interoperation.
|
|
||||||
func (f ID) Base32() string {
|
|
||||||
|
|
||||||
if f < 32 {
|
|
||||||
return string(encodeBase32Map[f])
|
|
||||||
}
|
|
||||||
|
|
||||||
b := make([]byte, 0, 12)
|
|
||||||
for f >= 32 {
|
|
||||||
b = append(b, encodeBase32Map[f%32])
|
|
||||||
f /= 32
|
|
||||||
}
|
|
||||||
b = append(b, encodeBase32Map[f])
|
|
||||||
|
|
||||||
for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 {
|
|
||||||
b[x], b[y] = b[y], b[x]
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBase32 parses a base32 []byte into a snowflake ID
|
|
||||||
// NOTE: There are many different base32 implementations so becareful when
|
|
||||||
// doing any interoperation.
|
|
||||||
func ParseBase32(b []byte) (ID, error) {
|
|
||||||
|
|
||||||
var id int64
|
|
||||||
|
|
||||||
for i := range b {
|
|
||||||
if decodeBase32Map[b[i]] == 0xFF {
|
|
||||||
return -1, ErrInvalidBase32
|
|
||||||
}
|
|
||||||
id = id*32 + int64(decodeBase32Map[b[i]])
|
|
||||||
}
|
|
||||||
|
|
||||||
return ID(id), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base36 returns a base36 string of the snowflake ID
|
|
||||||
func (f ID) Base36() string {
|
|
||||||
return strconv.FormatInt(int64(f), 36)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBase36 converts a Base36 string into a snowflake ID
|
|
||||||
func ParseBase36(id string) (ID, error) {
|
|
||||||
i, err := strconv.ParseInt(id, 36, 64)
|
|
||||||
return ID(i), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base58 returns a base58 string of the snowflake ID
|
|
||||||
func (f ID) Base58() string {
|
|
||||||
|
|
||||||
if f < 58 {
|
|
||||||
return string(encodeBase58Map[f])
|
|
||||||
}
|
|
||||||
|
|
||||||
b := make([]byte, 0, 11)
|
|
||||||
for f >= 58 {
|
|
||||||
b = append(b, encodeBase58Map[f%58])
|
|
||||||
f /= 58
|
|
||||||
}
|
|
||||||
b = append(b, encodeBase58Map[f])
|
|
||||||
|
|
||||||
for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 {
|
|
||||||
b[x], b[y] = b[y], b[x]
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBase58 parses a base58 []byte into a snowflake ID
|
|
||||||
func ParseBase58(b []byte) (ID, error) {
|
|
||||||
|
|
||||||
var id int64
|
|
||||||
|
|
||||||
for i := range b {
|
|
||||||
if decodeBase58Map[b[i]] == 0xFF {
|
|
||||||
return -1, ErrInvalidBase58
|
|
||||||
}
|
|
||||||
id = id*58 + int64(decodeBase58Map[b[i]])
|
|
||||||
}
|
|
||||||
|
|
||||||
return ID(id), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base64 returns a base64 string of the snowflake ID
|
|
||||||
func (f ID) Base64() string {
|
|
||||||
return base64.StdEncoding.EncodeToString(f.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBase64 converts a base64 string into a snowflake ID
|
|
||||||
func ParseBase64(id string) (ID, error) {
|
|
||||||
b, err := base64.StdEncoding.DecodeString(id)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
return ParseBytes(b)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns a byte slice of the snowflake ID
|
|
||||||
func (f ID) Bytes() []byte {
|
|
||||||
return []byte(f.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBytes converts a byte slice into a snowflake ID
|
|
||||||
func ParseBytes(id []byte) (ID, error) {
|
|
||||||
i, err := strconv.ParseInt(string(id), 10, 64)
|
|
||||||
return ID(i), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntBytes returns an array of bytes of the snowflake ID, encoded as a
|
|
||||||
// big endian integer.
|
|
||||||
func (f ID) IntBytes() [8]byte {
|
|
||||||
var b [8]byte
|
|
||||||
binary.BigEndian.PutUint64(b[:], uint64(f))
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseIntBytes converts an array of bytes encoded as big endian integer as
|
|
||||||
// a snowflake ID
|
|
||||||
func ParseIntBytes(id [8]byte) ID {
|
|
||||||
return ID(int64(binary.BigEndian.Uint64(id[:])))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time returns an int64 unix timestamp in milliseconds of the snowflake ID time
|
|
||||||
// DEPRECATED: the below function will be removed in a future release.
|
|
||||||
func (f ID) Time() int64 {
|
|
||||||
return (int64(f) >> timeShift) + Epoch
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node returns an int64 of the snowflake ID node number
|
|
||||||
// DEPRECATED: the below function will be removed in a future release.
|
|
||||||
func (f ID) Node() int64 {
|
|
||||||
return int64(f) & nodeMask >> nodeShift
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step returns an int64 of the snowflake step (or sequence) number
|
|
||||||
// DEPRECATED: the below function will be removed in a future release.
|
|
||||||
func (f ID) Step() int64 {
|
|
||||||
return int64(f) & stepMask
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON returns a json byte array string of the snowflake ID.
|
|
||||||
func (f ID) MarshalJSON() ([]byte, error) {
|
|
||||||
buff := make([]byte, 0, 22)
|
|
||||||
buff = append(buff, '"')
|
|
||||||
buff = strconv.AppendInt(buff, int64(f), 10)
|
|
||||||
buff = append(buff, '"')
|
|
||||||
return buff, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON converts a json byte array of a snowflake ID into an ID type.
|
|
||||||
func (f *ID) UnmarshalJSON(b []byte) error {
|
|
||||||
if len(b) < 3 || b[0] != '"' || b[len(b)-1] != '"' {
|
|
||||||
return JSONSyntaxError{b}
|
|
||||||
}
|
|
||||||
|
|
||||||
i, err := strconv.ParseInt(string(b[1:len(b)-1]), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*f = ID(i)
|
|
||||||
return nil
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user