Browse Source

Move apiserv

Volodymyr Tkach 2 years ago
parent
commit
ee0f020cdc

+ 153 - 0
utils/http/apiserv/apiserv.go

@@ -0,0 +1,153 @@
+package apiserv
+
+import (
+	"net/http"
+	"regexp"
+	"strings"
+
+	"github.com/vladimirok5959/golang-utils/utils/http/logger"
+)
+
+var mParam = regexp.MustCompile(`\{([^/]*)}`)
+
+var mParams = &Params{
+	list: map[*http.Request][]Param{},
+}
+
+type ServeMux struct {
+	handlers *Handlers
+}
+
+func NewServeMux() *ServeMux {
+	s := ServeMux{
+		handlers: &Handlers{
+			list: map[*regexp.Regexp]Handler{},
+		},
+	}
+	return &s
+}
+
+func GetParams(r *http.Request) []Param {
+	mParams.Lock()
+	defer mParams.Unlock()
+	if v, ok := mParams.list[r]; ok {
+		return v
+	}
+	return []Param{}
+}
+
+func (s ServeMux) regexp(pattern string) string {
+	pattern = strings.ReplaceAll(pattern, "-", "\\-")
+	pattern = strings.ReplaceAll(pattern, ".", "\\.")
+	pattern = strings.ReplaceAll(pattern, "/", "\\/")
+	pattern = mParam.ReplaceAllStringFunc(pattern, func(str string) string {
+		if str == "{i}" {
+			return "([\\d]+)"
+		} else if str == "{s}" {
+			return "([^\\/]+)"
+		}
+		return str
+	})
+	return "(?i)^" + pattern + "$"
+}
+
+func (s ServeMux) Delete(pattern string, handler http.Handler) {
+	s.Handle(pattern, []string{http.MethodDelete}, handler)
+}
+
+func (s ServeMux) Get(pattern string, handler http.Handler) {
+	s.Handle(pattern, []string{http.MethodGet}, handler)
+}
+
+func (s ServeMux) Handle(pattern string, methods []string, handler http.Handler) {
+	s.handlers.Lock()
+	defer s.handlers.Unlock()
+	if pattern != "" {
+		re := regexp.MustCompile(s.regexp(pattern))
+		s.handlers.list[re] = Handler{
+			handler: handler,
+			methods: methods,
+		}
+	} else {
+		s.handlers.list[nil] = Handler{
+			handler: handler,
+			methods: methods,
+		}
+	}
+}
+
+func (s ServeMux) NotFound(handler http.Handler) {
+	s.Handle("", []string{http.MethodGet}, handler)
+}
+
+func (s ServeMux) Options(pattern string, handler http.Handler) {
+	s.Handle(pattern, []string{http.MethodOptions}, handler)
+}
+
+func (s ServeMux) Patch(pattern string, handler http.Handler) {
+	s.Handle(pattern, []string{http.MethodPatch}, handler)
+}
+
+func (s ServeMux) Post(pattern string, handler http.Handler) {
+	s.Handle(pattern, []string{http.MethodPost}, handler)
+}
+
+func (s ServeMux) Put(pattern string, handler http.Handler) {
+	s.Handle(pattern, []string{http.MethodPut}, handler)
+}
+
+func (s ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	s.handlers.Lock()
+	defer s.handlers.Unlock()
+	for re, h := range s.handlers.list {
+		if re != nil && re.Match([]byte(r.URL.Path)) {
+			if h.IsMethod(r.Method) {
+				if rs := re.FindAllStringSubmatch(r.URL.Path, 1); len(rs) >= 1 {
+					defer func() {
+						mParams.Lock()
+						delete(mParams.list, r)
+						defer mParams.Unlock()
+					}()
+					mParams.Lock()
+					for _, p := range rs[0] {
+						if _, ok := mParams.list[r]; !ok {
+							mParams.list[r] = []Param{{value: p}}
+						} else {
+							mParams.list[r] = append(mParams.list[r], Param{value: p})
+						}
+						select {
+						case <-r.Context().Done():
+							mParams.Unlock()
+							return
+						default:
+						}
+					}
+					mParams.Unlock()
+				}
+				select {
+				case <-r.Context().Done():
+					return
+				default:
+				}
+				logger.LogRequests(h.handler).ServeHTTP(w, r)
+				return
+			} else {
+				w.WriteHeader(http.StatusMethodNotAllowed)
+				return
+			}
+		}
+		select {
+		case <-r.Context().Done():
+			return
+		default:
+		}
+	}
+
+	// Error 404
+	if h, ok := s.handlers.list[nil]; ok {
+		logger.LogRequests(h.handler).ServeHTTP(w, r)
+		return
+	}
+
+	w.WriteHeader(http.StatusNotFound)
+}

+ 17 - 0
utils/http/apiserv/handler.go

@@ -0,0 +1,17 @@
+package apiserv
+
+import "net/http"
+
+type Handler struct {
+	handler http.Handler
+	methods []string
+}
+
+func (s Handler) IsMethod(method string) bool {
+	for _, v := range s.methods {
+		if v == method {
+			return true
+		}
+	}
+	return false
+}

+ 11 - 0
utils/http/apiserv/handlers.go

@@ -0,0 +1,11 @@
+package apiserv
+
+import (
+	"regexp"
+	"sync"
+)
+
+type Handlers struct {
+	sync.RWMutex
+	list map[*regexp.Regexp]Handler
+}

+ 18 - 0
utils/http/apiserv/param.go

@@ -0,0 +1,18 @@
+package apiserv
+
+import "strconv"
+
+type Param struct {
+	value string
+}
+
+func (p Param) Integer() int64 {
+	if v, err := strconv.ParseInt(p.value, 10, 64); err == nil {
+		return v
+	}
+	return 0
+}
+
+func (p Param) String() string {
+	return p.value
+}

+ 11 - 0
utils/http/apiserv/params.go

@@ -0,0 +1,11 @@
+package apiserv
+
+import (
+	"net/http"
+	"sync"
+)
+
+type Params struct {
+	sync.RWMutex
+	list map[*http.Request][]Param
+}