123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- package rest
- import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "sync/atomic"
- "time"
- "git.ali33.ru/fcg-xvii/go-tools/json"
- "github.com/dgrijalva/jwt-go"
- )
- // IServer реализует интерфейс сервера для рест апи
- type IServer interface {
- // Addr возвращеат адрес, который сервер слушает
- Addr() string
- // Secret возвращает секретный ключ, при помощи которого генерируются токены авторизации пользователей
- Secret() []byte
- // HandleFunc устанавливает функцию обработчик, которая будет вызвана по обращению к URL, удовлетворяющему pattern
- HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
- // Listen запускает прослушивание сервера. Если не удалось успешно запустить прослушивание через
- // заданный временной интервал (timeout), будет возвращена ошибка
- Listen(timeout time.Duration, ctxParent context.Context) error
- // TokenGenerate - функция генерации токена авторизации
- TokenGenerate(m json.Map, expire int64) (string, error)
- // TokenParse - функция для расшифровки токена в карту значений
- TokenParse(token string) (json.Map, error)
- // Close останавливает прослушивание
- Close() error
- // Context возвращает контекст сервера
- Context() context.Context
- }
- // NewServer создает сервер для реализации рест апи
- func NewServer(addr string, secret []byte) IServer {
- return &Server{
- secret: secret,
- server: &http.Server{
- Addr: addr,
- Handler: http.NewServeMux(),
- },
- }
- }
- func NewServerTLS(addr string, secret []byte, tlsKey, tlsCert string) IServer {
- serv := NewServer(addr, secret).(*Server)
- serv.tlsEnabled = true
- serv.tlsKey = tlsKey
- serv.tlsCert = tlsCert
- return serv
- }
- // Server реализует сервер для рест апи
- type Server struct {
- secret []byte
- server *http.Server
- opened atomic.Bool
- ctx context.Context
- cancel context.CancelFunc
- tlsEnabled bool
- tlsKey string
- tlsCert string
- }
- // Context возвращает контекст сервера
- func (s *Server) Context() context.Context {
- return s.ctx
- }
- // Close останавливает прослушивание
- func (s *Server) Close() error {
- if !s.opened.Load() {
- return errors.New("ErrNotOpened")
- }
- s.cancel()
- return nil
- }
- // Listen запускает прослушивание сервера. Если не удалось успешно запустить прослушивание через
- // заданный временной интервал (timeout), будет возвращена ошибка
- func (s *Server) Listen(timeout time.Duration, ctxParent context.Context) (err error) {
- if s.opened.Swap(true) {
- return errors.New("ErrAlreadyOpened")
- }
- ctx, _ := context.WithTimeout(ctxParent, timeout)
- go func() {
- var cancel context.CancelFunc
- s.ctx, cancel = context.WithCancel(ctxParent)
- if !s.tlsEnabled {
- err = s.server.ListenAndServe()
- } else {
- err = s.server.ListenAndServeTLS(s.tlsCert, s.tlsKey)
- }
- s.opened.Store(false)
- cancel()
- }()
- <-ctx.Done()
- if err == nil {
- go func() {
- <-s.ctx.Done()
- s.server.Close()
- }()
- }
- return
- }
- // TokenGenerate - функция генерации токена авторизации
- func (s *Server) TokenGenerate(m json.Map, expire int64) (string, error) {
- token := jwt.New(jwt.SigningMethodHS256)
- claims := token.Claims.(jwt.MapClaims)
- for key, val := range m {
- claims[key] = val
- }
- if expire > 0 {
- claims["exp"] = time.Now().Add(time.Minute * 30).Unix()
- }
- tokenString, err := token.SignedString(s.Secret())
- return tokenString, err
- }
- func (s *Server) TokenParse(tokenString string) (json.Map, error) {
- token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
- if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
- return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
- }
- return s.Secret(), nil
- })
- if err != nil {
- return nil, err
- }
- if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
- return json.Map(claims), nil
- }
- return nil, errors.New("invalid token")
- }
- // Addr возвращеат адрес, который сервер слушает
- func (s *Server) Addr() string {
- return s.server.Addr
- }
- // Secret возвращает секретный ключ, при помощи которого генерируются токены авторизации пользователей
- func (s *Server) Secret() []byte {
- return s.secret
- }
- // HandleFunc устанавливает функцию обработчик, которая будет вызвана по обращению к URL, удовлетворяющему pattern
- func (s *Server) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
- s.server.Handler.(*http.ServeMux).HandleFunc(pattern, handler)
- }
|