mirror of
				https://github.com/mochi-mqtt/server.git
				synced 2025-10-31 19:42:38 +08:00 
			
		
		
		
	Progress on persistence
This commit is contained in:
		| @@ -12,7 +12,6 @@ import ( | |||||||
|  |  | ||||||
| 	mqtt "github.com/mochi-co/mqtt/server" | 	mqtt "github.com/mochi-co/mqtt/server" | ||||||
| 	"github.com/mochi-co/mqtt/server/listeners" | 	"github.com/mochi-co/mqtt/server/listeners" | ||||||
| 	"github.com/mochi-co/mqtt/server/listeners/auth" |  | ||||||
| 	//	_ "net/http/pprof" | 	//	_ "net/http/pprof" | ||||||
| 	//	"runtime/trace" | 	//	"runtime/trace" | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										66
									
								
								examples/persistence/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								examples/persistence/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"os/signal" | ||||||
|  | 	"syscall" | ||||||
|  |  | ||||||
|  | 	"github.com/logrusorgru/aurora" | ||||||
|  |  | ||||||
|  | 	mqtt "github.com/mochi-co/mqtt/server" | ||||||
|  | 	"github.com/mochi-co/mqtt/server/listeners" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  |  | ||||||
|  | 	sigs := make(chan os.Signal, 1) | ||||||
|  | 	done := make(chan bool, 1) | ||||||
|  | 	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) | ||||||
|  | 	go func() { | ||||||
|  | 		<-sigs | ||||||
|  | 		done <- true | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	fmt.Println(aurora.Magenta("Mochi MQTT Server initializing..."), aurora.Cyan("Persistence")) | ||||||
|  |  | ||||||
|  | 	server := mqtt.New() | ||||||
|  | 	tcp := listeners.NewTCP("t1", ":1883") | ||||||
|  | 	err := server.AddListener(tcp, &listeners.Config{ | ||||||
|  | 		Auth: new(Auth), | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Start broker... | ||||||
|  | 	go server.Serve() | ||||||
|  | 	fmt.Println(aurora.BgMagenta("  Started!  ")) | ||||||
|  |  | ||||||
|  | 	// Wait for signals... | ||||||
|  | 	<-done | ||||||
|  | 	fmt.Println(aurora.BgRed("  Caught Signal  ")) | ||||||
|  |  | ||||||
|  | 	// End gracefully. | ||||||
|  | 	server.Close() | ||||||
|  | 	fmt.Println(aurora.BgGreen("  Finished  ")) | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Auth is an example auth provider for the server. | ||||||
|  | type Auth struct{} | ||||||
|  |  | ||||||
|  | // Auth returns true if a username and password are acceptable. | ||||||
|  | // Auth always returns true. | ||||||
|  | func (a *Auth) Authenticate(user, password []byte) bool { | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ACL returns true if a user has access permissions to read or write on a topic. | ||||||
|  | // ACL is used to deny access to a specific topic to satisfy Test.test_subscribe_failure. | ||||||
|  | func (a *Auth) ACL(user []byte, topic string, write bool) bool { | ||||||
|  | 	if topic == "test/nosubscribe" { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
| @@ -54,7 +54,7 @@ func main() { | |||||||
| 		done <- true | 		done <- true | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	fmt.Println(aurora.Magenta("Mochi MQTT Broker initializing...")) | 	fmt.Println(aurora.Magenta("Mochi MQTT Server initializing..."), aurora.Cyan("TLS/SSL")) | ||||||
|  |  | ||||||
| 	server := mqtt.New() | 	server := mqtt.New() | ||||||
| 	tcp := listeners.NewTCP("t1", ":1883") | 	tcp := listeners.NewTCP("t1", ":1883") | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,15 +3,15 @@ module github.com/mochi-co/mqtt | |||||||
| go 1.13 | go 1.13 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect | 	github.com/asdine/storm v2.1.2+incompatible | ||||||
| 	github.com/gobwas/pool v0.2.0 // indirect | 	github.com/asdine/storm/v3 v3.1.0 | ||||||
| 	github.com/gobwas/ws v1.0.2 |  | ||||||
| 	github.com/gorilla/websocket v1.4.1 | 	github.com/gorilla/websocket v1.4.1 | ||||||
| 	github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a | 	github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a | ||||||
| 	github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23 | 	github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23 | ||||||
| 	github.com/rs/xid v1.2.1 | 	github.com/rs/xid v1.2.1 | ||||||
| 	github.com/stretchr/testify v1.4.0 | 	github.com/stretchr/testify v1.4.0 | ||||||
| 	golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 | 	go.etcd.io/bbolt v1.3.3 | ||||||
|  | 	golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect | ||||||
| ) | ) | ||||||
|  |  | ||||||
| replace github.com/mochi-co/debug => /Users/mochimochi/Development/Go/src/github.com/mochi-co/debug | replace github.com/mochi-co/debug => /Users/mochimochi/Development/Go/src/github.com/mochi-co/debug | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,15 +1,29 @@ | |||||||
|  | github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= | ||||||
|  | github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= | ||||||
|  | github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM= | ||||||
|  | github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= | ||||||
|  | github.com/asdine/storm v2.1.2+incompatible h1:dczuIkyqwY2LrtXPz8ixMrU/OFgZp71kbKTHGrXYt/Q= | ||||||
|  | github.com/asdine/storm v2.1.2+incompatible/go.mod h1:RarYDc9hq1UPLImuiXK3BIWPJLdIygvV3PsInK0FbVQ= | ||||||
|  | github.com/asdine/storm/v3 v3.1.0 h1:yrpSNS+E7ef5Y5KjyZDeyW72Dl17lYG7oZ7eUoWvo5s= | ||||||
|  | github.com/asdine/storm/v3 v3.1.0/go.mod h1:letAoLCXz4UfodwNgMNILMb2oRH+su337ZfHnkRzqDA= | ||||||
| github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||||
| github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
| github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= | ||||||
| github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
| github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= | ||||||
|  | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||||
| github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= | ||||||
| github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||||
| github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= | ||||||
| github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= | ||||||
|  | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | ||||||
|  | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||||
|  | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||||
|  | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | ||||||
|  | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||||
| github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23 h1:Wp7NjqGKGN9te9N/rvXYRhlVcrulGdxnz8zadXWs7fc= | github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23 h1:Wp7NjqGKGN9te9N/rvXYRhlVcrulGdxnz8zadXWs7fc= | ||||||
| github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= | github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= | ||||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||||
| @@ -17,14 +31,29 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN | |||||||
| github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= | github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= | ||||||
| github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= | ||||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
|  | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||||
| github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | ||||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||||
|  | github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= | ||||||
|  | github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= | ||||||
|  | go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= | ||||||
|  | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= | ||||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
|  | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||||
|  | golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
| golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= | ||||||
| golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
|  | golang.org/x/sys v0.0.0-20191105142833-ac3223d80179 h1:IqVhUQp5B9ARnZUcfqXy6zP+A+YuPpP7IFo8gFeCOzU= | ||||||
|  | golang.org/x/sys v0.0.0-20191105142833-ac3223d80179/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
|  | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||||
|  | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||||
|  | google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= | ||||||
|  | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
|  | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | ||||||
|  | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|   | |||||||
							
								
								
									
										140
									
								
								server/persistence/bolt/bolt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								server/persistence/bolt/bolt.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | package bolt | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	//"encoding/gob" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	sgob "github.com/asdine/storm/codec/gob" | ||||||
|  | 	"github.com/asdine/storm/v3" | ||||||
|  | 	"go.etcd.io/bbolt" | ||||||
|  |  | ||||||
|  | 	"github.com/mochi-co/mqtt/server/persistence" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	defaultPath    = "mochi.db" | ||||||
|  | 	defaultTimeout = 250 * time.Millisecond | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Store is a backend for writing and reading to bolt persistent storage. | ||||||
|  | type Store struct { | ||||||
|  | 	path string         // the path on which to store the db file. | ||||||
|  | 	opts *bbolt.Options // options for configuring the boltdb instance. | ||||||
|  | 	db   *storm.DB      // the boltdb instance. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // init registers storage structs in gob. | ||||||
|  | func init() { | ||||||
|  | 	//gob.Register(map[string]interface{}{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New returns a configured instance of the boltdb store. | ||||||
|  | func New(path string, opts *bbolt.Options) *Store { | ||||||
|  | 	if path == "" || path == "." { | ||||||
|  | 		path = defaultPath | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if opts == nil { | ||||||
|  | 		opts = &bbolt.Options{ | ||||||
|  | 			Timeout: defaultTimeout, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &Store{ | ||||||
|  | 		path: path, | ||||||
|  | 		opts: opts, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Open opens the boltdb instance. | ||||||
|  | func (s *Store) Open() error { | ||||||
|  | 	var err error | ||||||
|  | 	s.db, err = storm.Open(s.path, storm.BoltOptions(0600, s.opts), storm.Codec(sgob.Codec)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Close closes the boltdb instance. | ||||||
|  | func (s *Store) Close() { | ||||||
|  | 	s.db.Close() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // StoreSubscriptions writes all subscriptions to the boltdb instance as | ||||||
|  | // a bulk operation. | ||||||
|  | func (s *Store) StoreSubscriptions() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // StoreClients writes all clients to the boltdb instance as a bulk operation. | ||||||
|  | func (s *Store) StoreClients() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // StoreInflight writes all inflight messages to the boltdb instance as a bulk operation. | ||||||
|  | func (s *Store) StoreInflight() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // StoreInflight writes all inflight messages to the boltdb instance as a bulk operation. | ||||||
|  | func (s *Store) StoreRetained() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // StoreServerInfo writes the server info to the boltdb instance. | ||||||
|  | func (s *Store) StoreServerInfo(v persistence.ServerInfo) error { | ||||||
|  | 	err := s.db.Save(&v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WriteSubscription writes a single subscription to the boltdb instance. | ||||||
|  | func (s *Store) WriteSubscription(v persistence.Subscription) error { | ||||||
|  | 	err := s.db.Save(&v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WriteInflight writes a single inflight message to the boltdb instance. | ||||||
|  | func (s *Store) WriteInflight() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WriteClient writes a single client to the boltdb instance. | ||||||
|  | func (s *Store) WriteClient() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LoadSubscriptions loads all the subscriptions from the boltdb instance. | ||||||
|  | func (s *Store) LoadSubscriptions() (v []persistence.Subscription, err error) { | ||||||
|  | 	err = s.db.Find("T", "subscription", &v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LoadClients loads all the clients from the boltdb instance. | ||||||
|  | func (s *Store) LoadClients() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LoadInflight loads all the inflight messages from the boltdb instance. | ||||||
|  | func (s *Store) LoadInflight() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LoadServerInfo loads the server info from the boltdb instance. | ||||||
|  | func (s *Store) LoadServerInfo() (v persistence.ServerInfo, err error) { | ||||||
|  | 	err = s.db.One("ID", "server_info", &v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										101
									
								
								server/persistence/bolt/bolt_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								server/persistence/bolt/bolt_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | package bolt | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/require" | ||||||
|  | 	"go.etcd.io/bbolt" | ||||||
|  |  | ||||||
|  | 	"github.com/mochi-co/mqtt/server/persistence" | ||||||
|  | 	"github.com/mochi-co/mqtt/server/system" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const tmpPath = "testbolt.db" | ||||||
|  |  | ||||||
|  | func teardown(t *testing.T) { | ||||||
|  | 	err := os.Remove(tmpPath) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNew(t *testing.T) { | ||||||
|  | 	s := New(tmpPath, &bbolt.Options{ | ||||||
|  | 		Timeout: 500 * time.Millisecond, | ||||||
|  | 	}) | ||||||
|  | 	require.NotNil(t, s) | ||||||
|  | 	require.Equal(t, tmpPath, s.path) | ||||||
|  | 	require.Equal(t, 500*time.Millisecond, s.opts.Timeout) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewNoPath(t *testing.T) { | ||||||
|  | 	s := New("", nil) | ||||||
|  | 	require.NotNil(t, s) | ||||||
|  | 	require.Equal(t, defaultPath, s.path) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewNoOpts(t *testing.T) { | ||||||
|  | 	s := New("", nil) | ||||||
|  | 	require.NotNil(t, s) | ||||||
|  | 	require.Equal(t, defaultTimeout, s.opts.Timeout) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOpen(t *testing.T) { | ||||||
|  | 	s := New(tmpPath, nil) | ||||||
|  | 	err := s.Open() | ||||||
|  | 	require.NoError(t, err) | ||||||
|  | 	defer teardown(t) | ||||||
|  | 	require.NotNil(t, s.db) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStoreAndRetrieveServerInfo(t *testing.T) { | ||||||
|  | 	s := New(tmpPath, nil) | ||||||
|  | 	err := s.Open() | ||||||
|  | 	require.NoError(t, err) | ||||||
|  | 	defer teardown(t) | ||||||
|  |  | ||||||
|  | 	v := system.Info{ | ||||||
|  | 		Version: "test", | ||||||
|  | 		Started: 100, | ||||||
|  | 	} | ||||||
|  | 	err = s.StoreServerInfo(persistence.ServerInfo{v, "server_info"}) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	r, err := s.LoadServerInfo() | ||||||
|  | 	require.NoError(t, err) | ||||||
|  | 	require.NotNil(t, r) | ||||||
|  | 	require.Equal(t, v.Version, r.Version) | ||||||
|  | 	require.Equal(t, v.Started, r.Started) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestWriteAndRetrieveSubscription(t *testing.T) { | ||||||
|  | 	s := New(tmpPath, nil) | ||||||
|  | 	err := s.Open() | ||||||
|  | 	require.NoError(t, err) | ||||||
|  | 	defer teardown(t) | ||||||
|  |  | ||||||
|  | 	v := persistence.Subscription{ | ||||||
|  | 		ID:     "test:a/b/c", | ||||||
|  | 		Client: "test", | ||||||
|  | 		Filter: "a/b/c", | ||||||
|  | 		QoS:    1, | ||||||
|  | 		T:      "subscription", | ||||||
|  | 	} | ||||||
|  | 	err = s.WriteSubscription(v) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	v2 := persistence.Subscription{ | ||||||
|  | 		ID:     "test:d/e/f", | ||||||
|  | 		Client: "test", | ||||||
|  | 		Filter: "d/e/f", | ||||||
|  | 		QoS:    2, | ||||||
|  | 		T:      "subscription", | ||||||
|  | 	} | ||||||
|  | 	err = s.WriteSubscription(v2) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	subs, err := s.LoadSubscriptions() | ||||||
|  | 	require.NoError(t, err) | ||||||
|  | 	require.Equal(t, 2, len(subs)) | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										160
									
								
								server/persistence/persistence.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								server/persistence/persistence.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | |||||||
|  | package persistence | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  |  | ||||||
|  | 	"github.com/mochi-co/mqtt/server/system" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Store is an interface which details a persistent storage connector. | ||||||
|  | type Store interface { | ||||||
|  | 	Open() error | ||||||
|  | 	Close() | ||||||
|  |  | ||||||
|  | 	WriteSubscription() // including retained | ||||||
|  | 	WriteClient() | ||||||
|  | 	WriteInFlight() | ||||||
|  | 	WriteRetained() | ||||||
|  |  | ||||||
|  | 	StoreSubscriptions() | ||||||
|  | 	StoreInFlight() | ||||||
|  | 	StoreClients() | ||||||
|  | 	StoreServerInfo(v ServerInfo) error | ||||||
|  |  | ||||||
|  | 	ReadSubscriptions() (v []Subscription, err error) | ||||||
|  | 	ReadInflight() | ||||||
|  | 	ReadClients() | ||||||
|  | 	ReadServerInfo() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ServerInfo contains information and statistics about the server. | ||||||
|  | type ServerInfo struct { | ||||||
|  | 	system.Info        // embed the system info struct. | ||||||
|  | 	ID          string // the storage key. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Subscription contains the details of a topic filter subscription. | ||||||
|  | type Subscription struct { | ||||||
|  | 	ID     string // the storage key. | ||||||
|  | 	T      string // the type of the stored data. | ||||||
|  | 	Client string // the id of the client who the subscription belongs to. | ||||||
|  | 	Filter string // the topic filter being subscribed to. | ||||||
|  | 	QoS    byte   // the desired QoS byte. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Message contains the details of a retained or inflight message. | ||||||
|  | type Message struct { | ||||||
|  | 	ID          string      // the storage key. | ||||||
|  | 	T           string      // the type of the stored data. | ||||||
|  | 	FixedHeader FixedHeader // the header properties of the message. | ||||||
|  | 	PacketID    uint16      // the unique id of the packet (if inflight). | ||||||
|  | 	TopicName   string      // the topic the message was sent to (if retained). | ||||||
|  | 	Payload     []byte      // the message payload (if retained). | ||||||
|  | 	Sent        int64       // the last time the message was sent (for retries) in unixtime (if inflight). | ||||||
|  | 	Resends     int         // the number of times the message was attempted to be sent (if inflight). | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // FixedHeader contains the fixed header properties of a message. | ||||||
|  | type FixedHeader struct { | ||||||
|  | 	Type      byte // the type of the packet (PUBLISH, SUBSCRIBE, etc) from bits 7 - 4 (byte 1). | ||||||
|  | 	Dup       bool // indicates if the packet was already sent at an earlier time. | ||||||
|  | 	Qos       byte // indicates the quality of service expected. | ||||||
|  | 	Retain    bool // whether the message should be retained. | ||||||
|  | 	Remaining int  // the number of remaining bytes in the payload. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | // Client contains client data that can be persistently stored. | ||||||
|  | type Client struct { | ||||||
|  | 	ID            string         // the id of the client | ||||||
|  | 	Listener      string         // the last known listener id for the client | ||||||
|  | 	Username      []byte         // the username the client authenticated with. | ||||||
|  | 	CleanSession  bool           // indicates if the client connected expecting a clean-session. | ||||||
|  | 	Subscriptions []Subscription // a list of the subscriptions the user has. | ||||||
|  | 	LWT           LWT            // the last-will-and-testament message for the client. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LWT contains details about a clients LWT payload. | ||||||
|  | type LWT struct { | ||||||
|  | 	Topic   string // the topic the will message shall be sent to. | ||||||
|  | 	Message []byte // the message that shall be sent when the client disconnects. | ||||||
|  | 	Qos     byte   // the quality of service desired. | ||||||
|  | 	Retain  bool   // indicates whether the will message should be retained | ||||||
|  | } | ||||||
|  |  | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * | ||||||
|  | * | ||||||
|  | * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | // MockStore is a mock storage backend for testing. | ||||||
|  | type MockStore struct { | ||||||
|  | 	FailOpen bool | ||||||
|  | 	Closed   bool | ||||||
|  | 	Opened   bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Open opens the storage instance. | ||||||
|  | func (s *MockStore) Open() error { | ||||||
|  | 	if s.FailOpen { | ||||||
|  | 		return errors.New("test") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.Opened = true | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Close closes the storage instance. | ||||||
|  | func (s *MockStore) Close() { | ||||||
|  | 	s.Closed = true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // StoreSubscriptions writes all subscriptions to the storage instance. | ||||||
|  | func (s *MockStore) StoreSubscriptions() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // StoreClients writes all clients to the storage instance. | ||||||
|  | func (s *MockStore) StoreClients() {} | ||||||
|  |  | ||||||
|  | // StoreInFlight writes all Inflight messages to the storage instance. | ||||||
|  | func (s *MockStore) StoreInflight() {} | ||||||
|  |  | ||||||
|  | // StoreRetained writes all Inflight messages to the storage instance. | ||||||
|  | func (s *MockStore) StoreRetained() {} | ||||||
|  |  | ||||||
|  | // StoreServerInfo writes the server info to the storage instance. | ||||||
|  | func (s *MockStore) StoreServerInfo(v ServerInfo) { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WriteSubscription writes a single subscription to the storage instance. | ||||||
|  | func (s *MockStore) WriteSubscription() {} | ||||||
|  |  | ||||||
|  | // WriteClient writes a single client to the storage instance. | ||||||
|  | func (s *MockStore) WriteClient() {} | ||||||
|  |  | ||||||
|  | // WriteInFlight writes a single InFlight message to the storage instance. | ||||||
|  | func (s *MockStore) WriteInflight() {} | ||||||
|  |  | ||||||
|  | // WriteRetained writes a single retained message to the storage instance. | ||||||
|  | func (s *MockStore) WriteRetained() {} | ||||||
|  |  | ||||||
|  | // LoadSubscriptions loads the subscriptions from the storage instance. | ||||||
|  | func (s *MockStore) LoadSubscriptions() (v []Subscription, err error) { | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LoadClients loads the clients from the storage instance. | ||||||
|  | func (s *MockStore) LoadClients() {} | ||||||
|  |  | ||||||
|  | // LoadInflight loads the inflight messages from the storage instance. | ||||||
|  | func (s *MockStore) LoadInflight() {} | ||||||
|  |  | ||||||
|  | // LoadServerInfo loads the server info from the storage instance. | ||||||
|  | func (s *MockStore) LoadServerInfo() (v ServerInfo, err error) { | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								server/persistence/persistence_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								server/persistence/persistence_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | package persistence | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/require" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestMockStoreOpen(t *testing.T) { | ||||||
|  | 	s := new(MockStore) | ||||||
|  | 	err := s.Open() | ||||||
|  | 	require.NoError(t, err) | ||||||
|  | 	require.Equal(t, true, s.Opened) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMockStoreOpenFail(t *testing.T) { | ||||||
|  | 	s := new(MockStore) | ||||||
|  | 	s.FailOpen = true | ||||||
|  | 	err := s.Open() | ||||||
|  | 	require.Error(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMockStoreClose(t *testing.T) { | ||||||
|  | 	s := new(MockStore) | ||||||
|  | 	s.Close() | ||||||
|  | 	require.Equal(t, true, s.Closed) | ||||||
|  | } | ||||||
| @@ -14,6 +14,7 @@ import ( | |||||||
| 	"github.com/mochi-co/mqtt/server/internal/topics" | 	"github.com/mochi-co/mqtt/server/internal/topics" | ||||||
| 	"github.com/mochi-co/mqtt/server/listeners" | 	"github.com/mochi-co/mqtt/server/listeners" | ||||||
| 	"github.com/mochi-co/mqtt/server/listeners/auth" | 	"github.com/mochi-co/mqtt/server/listeners/auth" | ||||||
|  | 	"github.com/mochi-co/mqtt/server/persistence" | ||||||
| 	"github.com/mochi-co/mqtt/server/system" | 	"github.com/mochi-co/mqtt/server/system" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -40,6 +41,7 @@ type Server struct { | |||||||
| 	Clients   *clients.Clients     // clients known to the broker. | 	Clients   *clients.Clients     // clients known to the broker. | ||||||
| 	Topics    *topics.Index        // an index of topic subscriptions and retained messages. | 	Topics    *topics.Index        // an index of topic subscriptions and retained messages. | ||||||
| 	System    *system.Info         // values commonly found in $SYS topics. | 	System    *system.Info         // values commonly found in $SYS topics. | ||||||
|  | 	Stores    []persistence.Store  // a slice of persistent storage backends. | ||||||
| 	sysTicker *time.Ticker         // the interval ticker for sending updating $SYS topics. | 	sysTicker *time.Ticker         // the interval ticker for sending updating $SYS topics. | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -54,6 +56,7 @@ func New() *Server { | |||||||
| 			Version: Version, | 			Version: Version, | ||||||
| 			Started: time.Now().Unix(), | 			Started: time.Now().Unix(), | ||||||
| 		}, | 		}, | ||||||
|  | 		Stores:    make([]persistence.Store, 0, 1), | ||||||
| 		sysTicker: time.NewTicker(SysTopicInterval * time.Millisecond), | 		sysTicker: time.NewTicker(SysTopicInterval * time.Millisecond), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -64,6 +67,24 @@ func New() *Server { | |||||||
| 	return s | 	return s | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // AddStore adds a persistent storage backend to the server. | ||||||
|  | func (s *Server) AddStore(p persistence.Store) error { | ||||||
|  | 	err := p.Open() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.Stores = append(s.Stores, p) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CloseStores closes down all storage backends. | ||||||
|  | func (s *Server) CloseStores() { | ||||||
|  | 	for _, v := range s.Stores { | ||||||
|  | 		v.Close() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // AddListener adds a new network listener to the server. | // AddListener adds a new network listener to the server. | ||||||
| func (s *Server) AddListener(listener listeners.Listener, config *listeners.Config) error { | func (s *Server) AddListener(listener listeners.Listener, config *listeners.Config) error { | ||||||
| 	if _, ok := s.Listeners.Get(listener.ID()); ok { | 	if _, ok := s.Listeners.Get(listener.ID()); ok { | ||||||
| @@ -513,10 +534,11 @@ func (s *Server) publishSysTopics() { | |||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Close attempts to gracefully shutdown the server, all listeners, and clients. | // Close attempts to gracefully shutdown the server, all listeners, clients, and stores. | ||||||
| func (s *Server) Close() error { | func (s *Server) Close() error { | ||||||
| 	close(s.done) | 	close(s.done) | ||||||
| 	s.Listeners.CloseAll(s.closeListenerClients) | 	s.Listeners.CloseAll(s.closeListenerClients) | ||||||
|  | 	s.CloseStores() | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ import ( | |||||||
| 	"github.com/mochi-co/mqtt/server/internal/topics" | 	"github.com/mochi-co/mqtt/server/internal/topics" | ||||||
| 	"github.com/mochi-co/mqtt/server/listeners" | 	"github.com/mochi-co/mqtt/server/listeners" | ||||||
| 	"github.com/mochi-co/mqtt/server/listeners/auth" | 	"github.com/mochi-co/mqtt/server/listeners/auth" | ||||||
|  | 	"github.com/mochi-co/mqtt/server/persistence" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const defaultPort = ":18882" | const defaultPort = ":18882" | ||||||
| @@ -36,6 +37,7 @@ func TestNew(t *testing.T) { | |||||||
| 	require.NotNil(t, s.Listeners) | 	require.NotNil(t, s.Listeners) | ||||||
| 	require.NotNil(t, s.Clients) | 	require.NotNil(t, s.Clients) | ||||||
| 	require.NotNil(t, s.Topics) | 	require.NotNil(t, s.Topics) | ||||||
|  | 	require.NotNil(t, s.Stores) | ||||||
| 	require.NotEmpty(t, s.System.Version) | 	require.NotEmpty(t, s.System.Version) | ||||||
| 	require.Equal(t, true, s.System.Started > 0) | 	require.Equal(t, true, s.System.Started > 0) | ||||||
| } | } | ||||||
| @@ -46,6 +48,58 @@ func BenchmarkNew(b *testing.B) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestServerAddStore(t *testing.T) { | ||||||
|  | 	s := New() | ||||||
|  | 	require.NotNil(t, s) | ||||||
|  |  | ||||||
|  | 	p := new(persistence.MockStore) | ||||||
|  | 	err := s.AddStore(p) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	require.Equal(t, 1, len(s.Stores)) | ||||||
|  | 	require.Equal(t, p, s.Stores[0]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestServerAddStoreFailure(t *testing.T) { | ||||||
|  | 	s := New() | ||||||
|  | 	require.NotNil(t, s) | ||||||
|  |  | ||||||
|  | 	p := new(persistence.MockStore) | ||||||
|  | 	p.FailOpen = true | ||||||
|  | 	err := s.AddStore(p) | ||||||
|  | 	require.Error(t, err) | ||||||
|  | 	require.Equal(t, 0, len(s.Stores)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkServerAddStore(b *testing.B) { | ||||||
|  | 	s := New() | ||||||
|  | 	p := new(persistence.MockStore) | ||||||
|  | 	for n := 0; n < b.N; n++ { | ||||||
|  | 		s.AddStore(p) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestServerCloseStores(t *testing.T) { | ||||||
|  | 	s := New() | ||||||
|  | 	require.NotNil(t, s) | ||||||
|  |  | ||||||
|  | 	p := new(persistence.MockStore) | ||||||
|  | 	err := s.AddStore(p) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	p2 := new(persistence.MockStore) | ||||||
|  | 	err = s.AddStore(p2) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	require.Equal(t, false, p.Closed) | ||||||
|  | 	require.Equal(t, false, p2.Closed) | ||||||
|  |  | ||||||
|  | 	s.CloseStores() | ||||||
|  |  | ||||||
|  | 	require.Equal(t, true, p.Closed) | ||||||
|  | 	require.Equal(t, true, p2.Closed) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestServerAddListener(t *testing.T) { | func TestServerAddListener(t *testing.T) { | ||||||
| 	s := New() | 	s := New() | ||||||
| 	require.NotNil(t, s) | 	require.NotNil(t, s) | ||||||
| @@ -1109,7 +1163,11 @@ func TestServerClose(t *testing.T) { | |||||||
| 	cl.Listener = "t1" | 	cl.Listener = "t1" | ||||||
| 	s.Clients.Add(cl) | 	s.Clients.Add(cl) | ||||||
|  |  | ||||||
| 	err := s.AddListener(listeners.NewMockListener("t1", ":1882"), nil) | 	p := new(persistence.MockStore) | ||||||
|  | 	err := s.AddStore(p) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	err = s.AddListener(listeners.NewMockListener("t1", ":1882"), nil) | ||||||
| 	require.NoError(t, err) | 	require.NoError(t, err) | ||||||
| 	s.Serve() | 	s.Serve() | ||||||
| 	time.Sleep(time.Millisecond) | 	time.Sleep(time.Millisecond) | ||||||
| @@ -1122,6 +1180,7 @@ func TestServerClose(t *testing.T) { | |||||||
| 	s.Close() | 	s.Close() | ||||||
| 	time.Sleep(time.Millisecond) | 	time.Sleep(time.Millisecond) | ||||||
| 	require.Equal(t, false, listener.(*listeners.MockListener).IsServing) | 	require.Equal(t, false, listener.(*listeners.MockListener).IsServing) | ||||||
|  | 	require.Equal(t, true, p.Closed) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestServerCloseClientLWT(t *testing.T) { | func TestServerCloseClientLWT(t *testing.T) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Mochi
					Mochi