0x4a52466c696e74 il y a 4 mois
Parent
commit
b000163f9a

+ 53 - 0
application/app_config.go

@@ -0,0 +1,53 @@
+package application
+
+import "git.ali33.ru/fcg-xvii/rest"
+
+func HTTPHeadersCrossOrigin() func() map[string]string {
+	return func() map[string]string {
+		return map[string]string{
+			"Access-Control-Allow-Origin":  "*",
+			"Access-Control-Allow-Methods": "POST, GET, OPTIONS, PUT, DELETE",
+			"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
+		}
+	}
+}
+
+type AppConfig struct {
+	SSL                bool
+	Addr               string
+	Http               bool
+	Websocket          bool
+	Secret             []byte
+	HttpPrefix         string
+	WebsocketPrefix    string
+	HTTPHeaders        func() map[string]string
+	Commands           func(string) (rest.IExecuter, bool)
+	TLSKeyPath         string
+	TLSCertPath        string
+	Core               any
+	OnSocketConnect    func(rest.IStream)
+	OnSocketDisconnect func(rest.IStream)
+}
+
+func (s *AppConfig) GetHttpPrefix() string {
+	if len(s.HttpPrefix) > 0 {
+		return s.HttpPrefix
+	}
+	return "/api/"
+}
+
+func (s *AppConfig) GetWebsocketPrefix() string {
+	if len(s.WebsocketPrefix) > 0 {
+		return s.WebsocketPrefix
+	}
+	return "/ws/"
+}
+
+func (s *AppConfig) GetHTTPHeaders() func() map[string]string {
+	if s.HTTPHeaders != nil {
+		return s.HTTPHeaders
+	}
+	return func() map[string]string {
+		return map[string]string{}
+	}
+}

+ 98 - 0
application/application.go

@@ -0,0 +1,98 @@
+package application
+
+import (
+	"context"
+	"log"
+	"sync/atomic"
+	"time"
+
+	"git.ali33.ru/fcg-xvii/rest"
+	"git.ali33.ru/fcg-xvii/rest/rest_http"
+	"git.ali33.ru/fcg-xvii/rest/rest_websocket"
+)
+
+func New(appConf *AppConfig, swaggerConf *SwaggerConf, ctx context.Context) rest.IApplication {
+	app := &Application{
+		conf: appConf,
+	}
+	if swaggerConf != nil && swaggerConf.Enabled {
+		app.swagger = NewSwagger(swaggerConf)
+		go app.swagger.Start(ctx)
+	}
+	go app.start(ctx)
+	return app
+}
+
+type Application struct {
+	conf    *AppConfig
+	swagger *Swagger
+	//core      any
+	ctx       context.Context
+	cancel    context.CancelFunc
+	chConnect chan rest.IStream
+}
+
+func (s *Application) start(ctx context.Context) (err error) {
+	s.ctx, s.cancel = context.WithCancel(ctx)
+	var server rest.IServer
+	if s.conf.SSL {
+		server = rest.NewServerTLS(s.conf.Addr, s.conf.Secret, s.conf.TLSKeyPath, s.conf.TLSCertPath)
+	} else {
+		server = rest.NewServer(s.conf.Addr, s.conf.Secret)
+	}
+	if s.conf.Http {
+		restServ := rest_http.New(s, s.conf.Core, s.conf.GetHTTPHeaders()())
+		restServ.Prepare(server, s.conf.GetHttpPrefix())
+		//log.Println("http part prepared...")
+	}
+	if s.conf.Websocket {
+		s.chConnect = make(chan rest.IStream)
+		ws := rest_websocket.New(s, s.conf.Core)
+		ws.Prepare(server, s.conf.GetWebsocketPrefix())
+		go s.work()
+		//log.Println("websocket engine prepared...")
+	}
+	if err = server.Listen(time.Second, s.ctx); err != nil {
+		s.cancel()
+	}
+	return
+}
+
+func (s *Application) Stop() {
+	s.cancel()
+}
+
+func (s *Application) work() {
+	var counter atomic.Int32
+	for {
+		select {
+		case <-s.ctx.Done():
+			return
+		case stream := <-s.chConnect:
+			log.Println("CONNECT-------", counter.Add(1))
+			if s.conf.OnSocketConnect != nil {
+				s.conf.OnSocketConnect(stream)
+			}
+			go func(rStream rest.IStream) {
+				<-rStream.Context().Done()
+				log.Println("DISCONNECT-------", counter.Add(-1))
+				if s.conf.OnSocketDisconnect != nil {
+					s.conf.OnSocketDisconnect(rStream)
+				}
+			}(stream)
+		}
+	}
+}
+
+func (s *Application) Executer(r rest.IRequestIn) (rest.IExecuter, bool) {
+	log.Println("EXECUTER", r.RCommand())
+	if command, check := s.conf.Commands(r.RCommand()); check {
+		log.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
+		return command, true
+	}
+	return nil, false
+}
+
+func (s *Application) Connect() chan<- rest.IStream {
+	return s.chConnect
+}

+ 72 - 0
application/swagger.go

@@ -0,0 +1,72 @@
+package application
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/go-chi/chi/v5"
+	"github.com/go-chi/cors"
+	httpSwagger "github.com/swaggo/http-swagger"
+)
+
+type SwaggerConf struct {
+	Enabled     bool
+	Addr        string
+	URI         string
+	Prefix      string
+	SSL         bool
+	TLSKeyPath  string
+	TLSCertPath string
+}
+
+func NewSwagger(conf *SwaggerConf) *Swagger {
+	swagger := &Swagger{
+		conf: conf,
+	}
+	return swagger
+}
+
+type Swagger struct {
+	conf   *SwaggerConf
+	ctx    context.Context
+	cancel context.CancelFunc
+	server *http.Server
+}
+
+func (s *Swagger) Start(ctx context.Context) {
+	r := chi.NewRouter()
+	cors := cors.New(cors.Options{
+		AllowedOrigins:   []string{"*"}, // Разрешить все домены, для продакшена лучше указать конкретные домены
+		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
+		AllowedHeaders:   []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
+		ExposedHeaders:   []string{"Link"},
+		AllowCredentials: false,
+		MaxAge:           300, // Максимальный срок предварительного кэширования (в секундах)
+	})
+	r.Use(cors.Handler)
+	// Настройка сервера для обслуживания содержимого директории docs
+	fileServer := http.FileServer(http.Dir("./docs"))
+	r.Handle("/docs/*", http.StripPrefix("/docs", fileServer))
+	r.Get(s.conf.Prefix, httpSwagger.Handler(
+		httpSwagger.URL(s.conf.URI), //The url pointing to API definition
+	))
+	s.server = &http.Server{
+		Addr:    s.conf.Addr,
+		Handler: r,
+	}
+	s.ctx, s.cancel = context.WithCancel(ctx)
+	go func() {
+		<-s.ctx.Done()
+		s.Stop()
+	}()
+	if s.conf.SSL {
+		s.server.ListenAndServeTLS(s.conf.TLSCertPath, s.conf.TLSKeyPath)
+	} else {
+		s.server.ListenAndServe()
+	}
+	//http.ListenAndServe(s.conf.Addr, r)
+}
+
+func (s *Swagger) Stop() {
+	s.cancel()
+}

+ 65 - 0
application/z_test.go

@@ -0,0 +1,65 @@
+package application_test
+
+import (
+	"context"
+	"strings"
+	"testing"
+	"time"
+
+	"git.ali33.ru/fcg-xvii/rest"
+	"git.ali33.ru/fcg-xvii/rest/application"
+)
+
+type Hello struct {
+	Name string `rest:"required"`
+}
+
+func (s *Hello) Validate(req rest.IRequestIn) rest.IRequestOut {
+	if s.Name = strings.TrimSpace(s.Name); len(s.Name) == 0 {
+		return req.OutError(rest.ErrorFiled("name", "expected not empty string"))
+	}
+	return nil
+}
+
+func (s *Hello) Execute(req rest.IRequestIn) rest.IRequestOut {
+	response := HelloResponse{
+		Message: "Hello, " + s.Name,
+	}
+	return rest.OutFileds(req, &response, nil)
+}
+
+type HelloResponse struct {
+	Message string `rest:"default"`
+}
+
+////////////////////////////
+
+var (
+	commands = map[string]func() rest.IExecuter{
+		"/api/hello": func() rest.IExecuter { return &Hello{} },
+	}
+)
+
+func Command(cName string) (rest.IExecuter, bool) {
+	if command, check := commands[cName]; check {
+		return command(), true
+	}
+	return nil, false
+}
+
+func TestApp(t *testing.T) {
+	conf := &application.AppConfig{
+		Addr:            "192.168.100.3:5000",
+		Http:            true,
+		Websocket:       false,
+		Secret:          []byte("secret"),
+		HttpPrefix:      "/api/",
+		WebsocketPrefix: "/ws/",
+		HTTPHeaders:     application.HTTPHeadersCrossOrigin(),
+		Commands:        Command,
+	}
+
+	app := application.New(conf, nil, context.Background())
+	t.Log(app)
+	<-time.After(time.Hour)
+}

+ 2 - 1
example/application.go

@@ -1,6 +1,7 @@
 package example_test
 
 import (
+	"context"
 	"log"
 	"sync"
 	"sync/atomic"
@@ -73,7 +74,7 @@ func ExampleNew() {
 	restServ := rest_http.New(App, App, nil)
 	restServ.Prepare(server, "/")
 	// пробуем запустить его. Если через секунду запуск не удался, будет возвращена ошибка
-	if err := server.Listen(time.Second); err != nil {
+	if err := server.Listen(time.Second, context.Background()); err != nil {
 		// ошибка запуска
 		log.Fatal(err)
 	}

+ 17 - 0
example_chat/users/tickers.go

@@ -0,0 +1,17 @@
+package users
+
+import (
+	"log"
+
+	"git.ali33.ru/fcg-xvii/go-tools/json"
+	"git.ali33.ru/fcg-xvii/rest"
+)
+
+type Tickers struct {
+	TickerIDS []string
+}
+
+func (s *Tickers) Execute(req rest.IRequestIn) rest.IRequestOut {
+	log.Println(s.TickerIDS)
+	return req.OutSuccess(json.Map{}, nil)
+}

+ 2 - 1
example_chat/zv_test.go

@@ -1,6 +1,7 @@
 package example_chat_test
 
 import (
+	"context"
 	"testing"
 	"time"
 
@@ -16,7 +17,7 @@ func TestChat(t *testing.T) {
 	core := chat.New()
 
 	server := rest.NewServer(addr, []byte("top-secret"))
-	if err := server.Listen(time.Second); err != nil {
+	if err := server.Listen(time.Second, context.Background()); err != nil {
 		t.Fatal(err)
 	}
 

+ 18 - 1
go.mod

@@ -5,7 +5,24 @@ go 1.20
 require (
 	git.ali33.ru/fcg-xvii/go-tools v0.0.0-20230529104008-2552c5121c91
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/go-chi/chi/v5 v5.0.11
+	github.com/go-chi/cors v1.2.1
 	github.com/gorilla/websocket v1.5.1
+	github.com/swaggo/http-swagger v1.3.4
 )
 
-require golang.org/x/net v0.17.0 // indirect
+require (
+	github.com/KyleBanks/depth v1.2.1 // indirect
+	github.com/go-openapi/jsonpointer v0.19.5 // indirect
+	github.com/go-openapi/jsonreference v0.20.0 // indirect
+	github.com/go-openapi/spec v0.20.6 // indirect
+	github.com/go-openapi/swag v0.19.15 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/mailru/easyjson v0.7.6 // indirect
+	github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect
+	github.com/swaggo/swag v1.8.1 // indirect
+	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/sys v0.13.0 // indirect
+	golang.org/x/tools v0.1.12 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+)

+ 68 - 0
go.sum

@@ -1,8 +1,76 @@
 git.ali33.ru/fcg-xvii/go-tools v0.0.0-20230529104008-2552c5121c91 h1:8N3j1V1Yx24uHwCp+LPOOdzdoRq3ad9tEIchHd6CZUI=
 git.ali33.ru/fcg-xvii/go-tools v0.0.0-20230529104008-2552c5121c91/go.mod h1:YbBhWFFNNQIKcRisQFnpVaN5KA+XHGImSU1Z/MuntqU=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
+github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
+github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
+github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
+github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
+github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ=
+github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
+github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc=
+github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
+github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww=
+github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ=
+github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI=
+github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 13 - 5
server.go

@@ -21,7 +21,7 @@ type IServer interface {
 	HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
 	// Listen запускает прослушивание сервера. Если не удалось успешно запустить прослушивание через
 	// заданный временной интервал (timeout), будет возвращена ошибка
-	Listen(timeout time.Duration) error
+	Listen(timeout time.Duration, ctxParent context.Context) error
 	// TokenGenerate - функция генерации токена авторизации
 	TokenGenerate(m json.Map, expire int64) (string, error)
 	// Close останавливает прослушивание
@@ -55,6 +55,7 @@ type Server struct {
 	server     *http.Server
 	opened     atomic.Bool
 	ctx        context.Context
+	cancel     context.CancelFunc
 	tlsEnabled bool
 	tlsKey     string
 	tlsCert    string
@@ -70,19 +71,20 @@ func (s *Server) Close() error {
 	if !s.opened.Load() {
 		return errors.New("ErrNotOpened")
 	}
-	return s.server.Close()
+	s.cancel()
+	return nil
 }
 
 // Listen запускает прослушивание сервера. Если не удалось успешно запустить прослушивание через
 // заданный временной интервал (timeout), будет возвращена ошибка
-func (s *Server) Listen(timeout time.Duration) (err error) {
+func (s *Server) Listen(timeout time.Duration, ctxParent context.Context) (err error) {
 	if s.opened.Swap(true) {
 		return errors.New("ErrAlreadyOpened")
 	}
-	ctx, _ := context.WithTimeout(context.Background(), timeout)
+	ctx, _ := context.WithTimeout(ctxParent, timeout)
 	go func() {
 		var cancel context.CancelFunc
-		s.ctx, cancel = context.WithCancel(context.Background())
+		s.ctx, cancel = context.WithCancel(ctxParent)
 		if !s.tlsEnabled {
 			err = s.server.ListenAndServe()
 		} else {
@@ -92,6 +94,12 @@ func (s *Server) Listen(timeout time.Duration) (err error) {
 		cancel()
 	}()
 	<-ctx.Done()
+	if err == nil {
+		go func() {
+			<-s.ctx.Done()
+			s.server.Close()
+		}()
+	}
 	return
 }
 

+ 4 - 0
z_test.go

@@ -246,3 +246,7 @@ func TestSerialize(t *testing.T) {
 	}
 	log.Println(ti.TickerIDS)
 }
+
+func TestServer(t *testing.T) {
+
+}