initial import
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
Service Service `json:"service" yaml:"service"`
|
||||
}
|
||||
|
||||
func NewConfig() (*Config, error) {
|
||||
var err error
|
||||
return &Config{
|
||||
Service: Service{
|
||||
Address: "0.0.0.0",
|
||||
Port: 1025,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
Address string `json:"address" yaml:"address"`
|
||||
Port int64 `json:"port" yaml:"port"`
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package database
|
||||
|
||||
type DatabaseParams struct {
|
||||
}
|
||||
|
||||
type Database struct {
|
||||
}
|
||||
|
||||
func NewDatabase(params *DatabaseParams) (*Database, error) {
|
||||
var err error
|
||||
return &Database{}, err
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package descr
|
||||
@@ -0,0 +1,14 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"mstore/app/operator"
|
||||
"mstore/app/router"
|
||||
)
|
||||
|
||||
func (hand *Handler) FileExists(rctx *router.Context) {
|
||||
hand.logg.Debugf("handle FileExists")
|
||||
|
||||
params := &operator.FileExistsParams{}
|
||||
res, _ := hand.oper.FileExists(params)
|
||||
rctx.SetStatus(res.Code)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"mstore/app/logger"
|
||||
"mstore/app/operator"
|
||||
)
|
||||
|
||||
type HandlerParams struct {
|
||||
Operator *operator.Operator
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
oper *operator.Operator
|
||||
logg *logger.Logger
|
||||
}
|
||||
|
||||
func NewHandler(params *HandlerParams) (*Handler, error) {
|
||||
var err error
|
||||
hand := &Handler{
|
||||
oper: params.Operator,
|
||||
}
|
||||
hand.logg = logger.NewLogger("handler")
|
||||
return hand, err
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"mstore/app/router"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Error bool `json:"error" yaml:"error"`
|
||||
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||
Result any `json:"result,omitempty" yaml:"result,result"`
|
||||
}
|
||||
|
||||
func (hand *Handler) SendResult(rctx *router.Context, result any) {
|
||||
response := &Response{
|
||||
Error: false,
|
||||
Result: result,
|
||||
}
|
||||
rctx.SendJSON(response)
|
||||
}
|
||||
|
||||
func (hand *Handler) SendError(rctx *router.Context, err error) {
|
||||
response := &Response{
|
||||
Error: true,
|
||||
Message: err.Error(),
|
||||
}
|
||||
rctx.SendJSON(response)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"mstore/app/operator"
|
||||
"mstore/app/router"
|
||||
)
|
||||
|
||||
func (hand *Handler) SendHello(rctx *router.Context) {
|
||||
hand.logg.Debugf("handle sendHello")
|
||||
|
||||
params := &operator.SendHelloParams{}
|
||||
res, _ := hand.oper.SendHello(params)
|
||||
hand.SendResult(rctx, res)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
mtx sync.Mutex
|
||||
output io.Writer = os.Stderr
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
subject string
|
||||
}
|
||||
|
||||
func NewLogger(subj string) *Logger {
|
||||
return &Logger{
|
||||
subject: subj,
|
||||
}
|
||||
}
|
||||
|
||||
func SetWriter(newOut io.Writer) {
|
||||
mtx.Lock()
|
||||
output = newOut
|
||||
mtx.Unlock()
|
||||
}
|
||||
|
||||
func (logg *Logger) Debugf(message string, args ...any) {
|
||||
logg.printf("debug", message, args...)
|
||||
}
|
||||
|
||||
func (logg *Logger) Infof(message string, args ...any) {
|
||||
logg.printf("info", message, args...)
|
||||
}
|
||||
|
||||
func (logg *Logger) Warningf(message string, args ...any) {
|
||||
logg.printf("warning", message, args...)
|
||||
}
|
||||
|
||||
func (logg *Logger) Errorf(message string, args ...any) {
|
||||
logg.printf("error", message, args...)
|
||||
}
|
||||
|
||||
func (logg *Logger) printf(level, message string, args ...any) {
|
||||
timestamp := time.Now().Format(time.RFC3339)
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
fmt.Fprintf(buffer, "%s %s.%s: ", timestamp, logg.subject, level)
|
||||
fmt.Fprintf(buffer, message, args...)
|
||||
fmt.Fprintf(buffer, "\n")
|
||||
mtx.Lock()
|
||||
fmt.Fprint(output, buffer.String())
|
||||
mtx.Unlock()
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
logg := NewLogger("test")
|
||||
logg.Debugf("foo: %s", "bar")
|
||||
}
|
||||
|
||||
func BenchmarkLoggerL(b *testing.B) {
|
||||
SetWriter(ioutil.Discard)
|
||||
logg := NewLogger("test")
|
||||
for i := 0; i < b.N; i++ {
|
||||
logg.Debugf("foo: %s", "bar")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLoggerP(b *testing.B) {
|
||||
SetWriter(ioutil.Discard)
|
||||
logg := NewLogger("test")
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logg.Debugf("foo: %s", "bar")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type FileExistsParams struct{}
|
||||
|
||||
type FileExistsResult struct {
|
||||
Code int
|
||||
}
|
||||
|
||||
func (oper *Operator) FileExists(param *FileExistsParams) (*FileExistsResult, error) {
|
||||
var err error
|
||||
res := &FileExistsResult{
|
||||
Code: http.StatusOK,
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"mstore/app/database"
|
||||
)
|
||||
|
||||
type OperatorParams struct {
|
||||
Database *database.Database
|
||||
}
|
||||
|
||||
type Operator struct {
|
||||
db *database.Database
|
||||
}
|
||||
|
||||
func NewOperator(params *OperatorParams) (*Operator, error) {
|
||||
var err error
|
||||
oper := &Operator{
|
||||
db: params.Database,
|
||||
}
|
||||
return oper, err
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package operator
|
||||
|
||||
type SendHelloParams struct{}
|
||||
|
||||
type SendHelloResult struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (oper *Operator) SendHello(param *SendHelloParams) (*SendHelloResult, error) {
|
||||
var err error
|
||||
res := &SendHelloResult{}
|
||||
res.Message = "hello"
|
||||
return res, err
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
Ctx context.Context
|
||||
Request *http.Request
|
||||
Writer http.ResponseWriter
|
||||
PathMap map[string]string
|
||||
}
|
||||
|
||||
func NewContext(writer http.ResponseWriter, request *http.Request) *Context {
|
||||
ctx := context.Background()
|
||||
rctx := &Context{
|
||||
Writer: writer,
|
||||
Request: request,
|
||||
Ctx: ctx,
|
||||
PathMap: make(map[string]string),
|
||||
}
|
||||
return rctx
|
||||
}
|
||||
|
||||
func (rctx *Context) SetStatus(httpStatus int) {
|
||||
rctx.Writer.WriteHeader(httpStatus)
|
||||
}
|
||||
|
||||
func (rctx *Context) SendJSON(payload any) {
|
||||
rctx.Writer.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(rctx.Writer).Encode(payload)
|
||||
}
|
||||
|
||||
func (rctx *Context) SendText(payload string) {
|
||||
rctx.Writer.Header().Set("Content-Type", "text/plain")
|
||||
rctx.Writer.Write([]byte(payload))
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
compContextPlain int = iota
|
||||
compContextRegex
|
||||
|
||||
startRegex byte = '{'
|
||||
stopRegex byte = '}'
|
||||
)
|
||||
|
||||
func pathCompiler(path string) (string, error) {
|
||||
var err error
|
||||
res := make([]byte, 0)
|
||||
var pos int = compContextPlain
|
||||
var depth int = 0
|
||||
pattern := make([]byte, 0)
|
||||
for _, b := range []byte(path) {
|
||||
switch pos {
|
||||
case compContextPlain:
|
||||
switch b {
|
||||
case stopRegex:
|
||||
depth -= 1
|
||||
res = append(res, b)
|
||||
case startRegex:
|
||||
depth += 1
|
||||
pos = compContextRegex // pattern started
|
||||
pattern = make([]byte, 0)
|
||||
default:
|
||||
res = append(res, b)
|
||||
}
|
||||
case compContextRegex:
|
||||
switch b {
|
||||
case startRegex:
|
||||
depth += 1
|
||||
case stopRegex:
|
||||
depth -= 1
|
||||
if depth == 0 {
|
||||
pattern = convertRegexp(pattern)
|
||||
res = append(res, pattern...)
|
||||
pos = compContextPlain // pattern ended
|
||||
}
|
||||
default:
|
||||
pattern = append(pattern, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
if depth != 0 {
|
||||
err = fmt.Errorf("Unbalanced brackets into pattern")
|
||||
}
|
||||
return string(res), err
|
||||
}
|
||||
|
||||
const (
|
||||
defaultRegexp = `[a-zA-Z0-9_][\-\.a-zA-Z0-9_%%=:~]+`
|
||||
)
|
||||
|
||||
func convertRegexp(src []byte) []byte {
|
||||
var res string
|
||||
const patternSeps = ":"
|
||||
parts := strings.SplitN(string(src), patternSeps, 2)
|
||||
if len(parts) == 1 {
|
||||
parts = append(parts, defaultRegexp)
|
||||
}
|
||||
res = fmt.Sprintf("(?<%s>%s)", parts[0], parts[1])
|
||||
return []byte(res)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func tDebugf(msg string, args ...any) {
|
||||
fmt.Printf("debug: ")
|
||||
fmt.Printf(msg, args...)
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func TestPatchCompilerA(t *testing.T) {
|
||||
var err error
|
||||
srcPath := `/v1/file/{collection:[a-zA-Z]+}/{name}`
|
||||
reSource, err := pathCompiler(srcPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
tDebugf("re: %s\n", reSource)
|
||||
|
||||
re, err := regexp.Compile(reSource)
|
||||
require.NoError(t, err)
|
||||
reqPath := `/v1/file/foo/bare`
|
||||
match := re.MatchString(reqPath)
|
||||
require.True(t, match)
|
||||
|
||||
submatch := re.FindStringSubmatch(reqPath)
|
||||
subnames := re.SubexpNames()
|
||||
|
||||
submap := make(map[string]string)
|
||||
for i, val := range subnames {
|
||||
tDebugf("subname: %d = %s", i, val)
|
||||
}
|
||||
for i, val := range submatch {
|
||||
key := subnames[i]
|
||||
if key != "" {
|
||||
submap[key] = val
|
||||
}
|
||||
tDebugf("sub: %d = %s", i, val)
|
||||
}
|
||||
tDebugf("submap: %v", submap)
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Handler interface {
|
||||
ServeHTTP(rctx *Context)
|
||||
}
|
||||
|
||||
type HandlerFunc func(rctx *Context)
|
||||
|
||||
func (handlerFunc HandlerFunc) ServeHTTP(rctx *Context) {
|
||||
handlerFunc(rctx)
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
routeHandler *Selector
|
||||
}
|
||||
|
||||
func NewRouter() *Router {
|
||||
return &Router{
|
||||
routeHandler: NewSelector(),
|
||||
}
|
||||
}
|
||||
|
||||
func (rout *Router) AddRoute(method, path string, handlerFunc HandlerFunc) {
|
||||
rout.routeHandler.AddRoute(method, path, handlerFunc)
|
||||
}
|
||||
|
||||
func (rout *Router) Get(path string, handlerFunc HandlerFunc) {
|
||||
rout.routeHandler.AddRoute("GET", path, handlerFunc)
|
||||
}
|
||||
|
||||
func (rout *Router) Post(path string, handlerFunc HandlerFunc) {
|
||||
rout.routeHandler.AddRoute("POST", path, handlerFunc)
|
||||
}
|
||||
|
||||
func (rout *Router) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
|
||||
rctx := NewContext(writer, req)
|
||||
rout.routeHandler.ServeHTTP(rctx)
|
||||
}
|
||||
|
||||
func (rout *Router) NotFound(handlerFunc HandlerFunc) {
|
||||
rout.routeHandler.notFound = handlerFunc
|
||||
}
|
||||
|
||||
type Selector struct {
|
||||
Routes []*Route
|
||||
notFound Handler
|
||||
}
|
||||
|
||||
func NewSelector() *Selector {
|
||||
notFound := HandlerFunc(func(ctx *Context) {
|
||||
http.NotFound(ctx.Writer, ctx.Request)
|
||||
})
|
||||
return &Selector{
|
||||
Routes: make([]*Route, 0),
|
||||
notFound: notFound,
|
||||
}
|
||||
}
|
||||
|
||||
const globalRoutePattern = `^%s$`
|
||||
|
||||
func (hand *Selector) AddRoute(method, path string, handlerFunc HandlerFunc) error {
|
||||
var err error
|
||||
path, err = pathCompiler(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = fmt.Sprintf(globalRoutePattern, path)
|
||||
re, err := regexp.Compile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
route := &Route{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Handler: handlerFunc,
|
||||
Regexp: re,
|
||||
}
|
||||
hand.Routes = append(hand.Routes, route)
|
||||
return err
|
||||
}
|
||||
|
||||
func (hand *Selector) ServeHTTP(rctx *Context) {
|
||||
realHandler := hand.notFound
|
||||
for _, route := range hand.Routes {
|
||||
match := route.Match(rctx.Request)
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
subvals := route.Regexp.FindStringSubmatch(rctx.Request.URL.Path)
|
||||
subkeys := route.Regexp.SubexpNames()
|
||||
for i, val := range subvals {
|
||||
key := subkeys[i]
|
||||
if key != "" {
|
||||
rctx.PathMap[key] = val
|
||||
}
|
||||
}
|
||||
realHandler = route.Handler
|
||||
break
|
||||
}
|
||||
realHandler.ServeHTTP(rctx)
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
Method string
|
||||
Regexp *regexp.Regexp
|
||||
Path string
|
||||
Handler HandlerFunc
|
||||
}
|
||||
|
||||
func (route Route) Match(req *http.Request) bool {
|
||||
if req.Method != route.Method {
|
||||
return false
|
||||
}
|
||||
match := route.Regexp.MatchString(req.URL.Path)
|
||||
if match {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRouterStatus(t *testing.T) {
|
||||
|
||||
reqPath := "/hello"
|
||||
handler := NewRouter()
|
||||
helloHandler := func(rctx *Context) {
|
||||
rctx.SetStatus(http.StatusOK)
|
||||
}
|
||||
handler.Get(reqPath, helloHandler)
|
||||
|
||||
request, err := http.NewRequest("GET", reqPath, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
require.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
||||
}
|
||||
|
||||
func TestRouterSendText(t *testing.T) {
|
||||
testText := "hello, world"
|
||||
reqPath := "/hello"
|
||||
rout := NewRouter()
|
||||
helloHandler := func(rctx *Context) {
|
||||
rctx.SendText(testText)
|
||||
}
|
||||
rout.Get(reqPath, helloHandler)
|
||||
|
||||
request, err := http.NewRequest("GET", reqPath, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
rout.ServeHTTP(recorder, request)
|
||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
||||
require.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
bodyReader := recorder.Body
|
||||
bodyBytes, err := io.ReadAll(bodyReader)
|
||||
|
||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
||||
require.Equal(t, string(bodyBytes), testText)
|
||||
}
|
||||
|
||||
func TestRouterSendJSON(t *testing.T) {
|
||||
|
||||
type testStruct struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
testVar := testStruct{
|
||||
Message: "hello, world",
|
||||
}
|
||||
testData, err := json.Marshal(testVar)
|
||||
require.NoError(t, err)
|
||||
|
||||
reqPath := "/hello"
|
||||
handler := NewRouter()
|
||||
helloHandler := func(rctx *Context) {
|
||||
rctx.SendJSON(&testVar)
|
||||
}
|
||||
handler.Get(reqPath, helloHandler)
|
||||
|
||||
request, err := http.NewRequest("GET", reqPath, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
||||
require.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
bodyReader := recorder.Body
|
||||
bodyBytes, err := io.ReadAll(bodyReader)
|
||||
require.NoError(t, err)
|
||||
bodyBytes = bytes.Trim(bodyBytes, "\n\r")
|
||||
|
||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
||||
require.Equal(t, string(testData), string(bodyBytes))
|
||||
}
|
||||
|
||||
func BenchmarkLoggerL(b *testing.B) {
|
||||
reqPath := "/hello"
|
||||
helloHandler := func(rctx *Context) {
|
||||
rctx.SetStatus(http.StatusOK)
|
||||
}
|
||||
handler := NewRouter()
|
||||
handler.Get(reqPath, helloHandler)
|
||||
|
||||
request, err := http.NewRequest("GET", reqPath, nil)
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
recorder := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
require.Equal(b, http.StatusOK, recorder.Code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
//"io/ioutil"
|
||||
"os"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
//"os/user"
|
||||
//"path/filepath"
|
||||
//"strconv"
|
||||
//"sync"
|
||||
"syscall"
|
||||
//"time"
|
||||
|
||||
"mstore/app/database"
|
||||
"mstore/app/handler"
|
||||
"mstore/app/logger"
|
||||
"mstore/app/operator"
|
||||
"mstore/app/service"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
oper *operator.Operator
|
||||
svc *service.Service
|
||||
db *database.Database
|
||||
hand *handler.Handler
|
||||
logg *logger.Logger
|
||||
}
|
||||
|
||||
func NewServer() (*Server, error) {
|
||||
var err error
|
||||
srv := &Server{}
|
||||
srv.logg = logger.NewLogger("server")
|
||||
return srv, err
|
||||
}
|
||||
|
||||
func (srv *Server) Handler() *handler.Handler {
|
||||
return srv.hand
|
||||
}
|
||||
|
||||
func (srv *Server) Configure() error {
|
||||
var err error
|
||||
srv.logg.Infof("Server configure")
|
||||
return err
|
||||
}
|
||||
|
||||
func (srv *Server) Build() error {
|
||||
var err error
|
||||
srv.logg.Infof("Server build")
|
||||
// Database create
|
||||
databaseParams := &database.DatabaseParams{}
|
||||
srv.db, err = database.NewDatabase(databaseParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Operator create
|
||||
operatorParams := &operator.OperatorParams{
|
||||
Database: srv.db,
|
||||
}
|
||||
srv.oper, err = operator.NewOperator(operatorParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Handler create
|
||||
handlerParams := &handler.HandlerParams{
|
||||
Operator: srv.oper,
|
||||
}
|
||||
srv.hand, err = handler.NewHandler(handlerParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Service create
|
||||
serviceParams := &service.ServiceParams{
|
||||
Handler: srv.hand,
|
||||
}
|
||||
srv.svc, err = service.NewService(serviceParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Service build
|
||||
err = srv.svc.Build()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (srv *Server) Run() error {
|
||||
var err error
|
||||
|
||||
currUser, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.logg.Infof("Server run as user %s", currUser.Username)
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
svcDone := make(chan error, 1)
|
||||
|
||||
// Service run
|
||||
startService := func(svc *service.Service, done chan error) {
|
||||
err = svc.Run()
|
||||
if err != nil {
|
||||
srv.logg.Errorf("Service error: %v", err)
|
||||
done <- err
|
||||
}
|
||||
}
|
||||
go startService(srv.svc, svcDone)
|
||||
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
var signal os.Signal
|
||||
|
||||
select {
|
||||
case signal = <-sigs:
|
||||
srv.logg.Infof("Services stopped by signal: %v", signal)
|
||||
srv.svc.Stop()
|
||||
case err = <-svcDone:
|
||||
srv.logg.Infof("Service stopped by service error: %v", err)
|
||||
srv.svc.Stop()
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
PathServiceHello = "/service/hello"
|
||||
)
|
||||
|
||||
var (
|
||||
pathRegexp = `{path:[a-zA-Z_][\-a-zA-Z0-9_\/\.%~]+}`
|
||||
fileOperPrefix = "/v3/file"
|
||||
|
||||
PathFileExists = fmt.Sprintf("%s/%s", fileOperPrefix, pathRegexp)
|
||||
)
|
||||
@@ -0,0 +1,74 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"mstore/app/handler"
|
||||
"mstore/app/logger"
|
||||
"mstore/app/router"
|
||||
)
|
||||
|
||||
const protocol = "tcp"
|
||||
|
||||
type ServiceParams struct {
|
||||
Handler *handler.Handler
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
hand *handler.Handler
|
||||
rout *router.Router
|
||||
logg *logger.Logger
|
||||
|
||||
address string
|
||||
portnum int64
|
||||
listen net.Listener
|
||||
hsrv *http.Server
|
||||
}
|
||||
|
||||
func NewService(params *ServiceParams) (*Service, error) {
|
||||
var err error
|
||||
svc := &Service{
|
||||
hand: params.Handler,
|
||||
}
|
||||
svc.logg = logger.NewLogger("service")
|
||||
return svc, err
|
||||
}
|
||||
|
||||
func (svc *Service) Build() error {
|
||||
var err error
|
||||
svc.logg.Infof("Service build ")
|
||||
|
||||
svc.rout = router.NewRouter()
|
||||
svc.rout.Get(PathServiceHello, svc.hand.SendHello)
|
||||
svc.rout.Get(PathFileExists, svc.hand.FileExists)
|
||||
|
||||
listenAddress := fmt.Sprintf("%s:%d", svc.address, svc.portnum)
|
||||
svc.listen, err = net.Listen(protocol, listenAddress)
|
||||
svc.hsrv = &http.Server{
|
||||
Handler: svc.rout,
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) Run() error {
|
||||
var err error
|
||||
svc.logg.Infof("Service run")
|
||||
err = svc.hsrv.Serve(svc.listen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) Stop() {
|
||||
svc.logg.Infof("Service stop")
|
||||
if svc.hsrv != nil {
|
||||
downWaiting := 5 * time.Second
|
||||
ctx, _ := context.WithTimeout(context.Background(), downWaiting)
|
||||
svc.hsrv.Shutdown(ctx)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package storage
|
||||
Reference in New Issue
Block a user