Araneae: Get Gin
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| [submodule "framework/gin"] | ||||
| 	path = framework/gin | ||||
| 	url = gitea@git.vinchent.xyz:vinchent/gin.git | ||||
| @ -1,124 +0,0 @@ | ||||
| package framework | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // Context type is the customized context of Araneae framework | ||||
| // | ||||
| // It packages the internal context.Context with basic "wr" couple. | ||||
| type Context struct { | ||||
| 	ctx            context.Context | ||||
| 	request        *http.Request | ||||
| 	responseWriter http.ResponseWriter | ||||
|  | ||||
| 	handlers []ControllerHandler | ||||
| 	// current handler index | ||||
| 	index int | ||||
|  | ||||
| 	params map[string]string | ||||
|  | ||||
| 	hasTimeout bool | ||||
| 	writerMux  *sync.Mutex | ||||
| } | ||||
|  | ||||
| // NewContext create a new context | ||||
| func NewContext(w http.ResponseWriter, r *http.Request) *Context { | ||||
| 	return &Context{ | ||||
| 		ctx:            r.Context(), | ||||
| 		request:        r, | ||||
| 		responseWriter: w, | ||||
| 		writerMux:      &sync.Mutex{}, | ||||
| 		index:          -1, // will be set to 0 when at the beginning | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // {{{ Basic functions | ||||
|  | ||||
| // WriterMux returns the writer mutex. | ||||
| // | ||||
| // This is useful when goroutines concurrently write into responseWriter, | ||||
| // while at the same time we are writing into the responseWriter for a | ||||
| // panic or timeout. | ||||
| // We can protect it at the context level. | ||||
| func (ctx *Context) WriterMux() *sync.Mutex { | ||||
| 	return ctx.writerMux | ||||
| } | ||||
|  | ||||
| // GetRequest returns the original request | ||||
| func (ctx *Context) GetRequest() *http.Request { | ||||
| 	return ctx.request | ||||
| } | ||||
|  | ||||
| // GetResponseWriter returns the original response writer | ||||
| func (ctx *Context) GetResponseWriter() http.ResponseWriter { | ||||
| 	return ctx.responseWriter | ||||
| } | ||||
|  | ||||
| // SetHasTimeout indicates that the context has timeout. | ||||
| // | ||||
| // So that other goroutines won't write into the responseWriter anymore | ||||
| func (ctx *Context) SetHasTimeout() { | ||||
| 	ctx.hasTimeout = true | ||||
| } | ||||
|  | ||||
| // HasTimeout returns whether the context has timeout. | ||||
| func (ctx *Context) HasTimeout() bool { | ||||
| 	return ctx.hasTimeout | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| // {{{ Implements context interface | ||||
|  | ||||
| // BaseContext return a request default Context | ||||
| func (ctx *Context) BaseContext() context.Context { | ||||
| 	return ctx.request.Context() | ||||
| } | ||||
|  | ||||
| // Done calls the base function | ||||
| func (ctx *Context) Done() <-chan struct{} { | ||||
| 	return ctx.BaseContext().Done() | ||||
| } | ||||
|  | ||||
| // Deadline calls the base function | ||||
| func (ctx *Context) Deadline() (deadline time.Time, ok bool) { | ||||
| 	return ctx.BaseContext().Deadline() | ||||
| } | ||||
|  | ||||
| // Err calls the base function | ||||
| func (ctx *Context) Err() error { | ||||
| 	return ctx.BaseContext().Err() | ||||
| } | ||||
|  | ||||
| // Value calls the base function | ||||
| func (ctx *Context) Value(key any) any { | ||||
| 	return ctx.BaseContext().Value(key) | ||||
| } | ||||
|  | ||||
| // Next runs the next function in the function chain | ||||
| func (ctx *Context) Next() error { | ||||
| 	ctx.index++ | ||||
| 	if ctx.index >= len(ctx.handlers) { | ||||
| 		// This is the end of the chain | ||||
| 		return nil | ||||
| 	} | ||||
| 	// Run this handler | ||||
| 	if err := ctx.handlers[ctx.index](ctx); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // SetHandlers sets handlers for context | ||||
| func (ctx *Context) SetHandlers(handlers []ControllerHandler) { | ||||
| 	ctx.handlers = handlers | ||||
| } | ||||
|  | ||||
| func (ctx *Context) SetParams(params map[string]string) { | ||||
| 	ctx.params = params | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| @ -1,3 +0,0 @@ | ||||
| package framework | ||||
|  | ||||
| type ControllerHandler func(c *Context) error | ||||
| @ -1,115 +0,0 @@ | ||||
| package framework | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Core is the core struct of the framework | ||||
| type Core struct { | ||||
| 	router      map[string]*Trie | ||||
| 	middlewares []ControllerHandler | ||||
| } | ||||
|  | ||||
| // NewCore initialize the Core. | ||||
| func NewCore() *Core { | ||||
| 	getRouter := NewTrie() | ||||
| 	postRouter := NewTrie() | ||||
| 	putRouter := NewTrie() | ||||
| 	deleteRouter := NewTrie() | ||||
|  | ||||
| 	router := map[string]*Trie{} | ||||
| 	router["GET"] = getRouter | ||||
| 	router["POST"] = postRouter | ||||
| 	router["PUT"] = putRouter | ||||
| 	router["DELETE"] = deleteRouter | ||||
|  | ||||
| 	return &Core{router: router} | ||||
| } | ||||
|  | ||||
| // Get is a simple get router | ||||
| func (c *Core) Get(url string, handlers ...ControllerHandler) { | ||||
| 	allHandlers := append(c.middlewares, handlers...) | ||||
| 	if err := c.router["GET"].AddRouter(url, allHandlers); err != nil { | ||||
| 		log.Println(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Post is a simple post router | ||||
| func (c *Core) Post(url string, handlers ...ControllerHandler) { | ||||
| 	allHandlers := append(c.middlewares, handlers...) | ||||
| 	if err := c.router["POST"].AddRouter(url, allHandlers); err != nil { | ||||
| 		log.Println(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Put is a simple put router | ||||
| func (c *Core) Put(url string, handlers ...ControllerHandler) { | ||||
| 	allHandlers := append(c.middlewares, handlers...) | ||||
| 	if err := c.router["PUT"].AddRouter(url, allHandlers); err != nil { | ||||
| 		log.Println(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Delete is a simple delete router | ||||
| func (c *Core) Delete(url string, handlers ...ControllerHandler) { | ||||
| 	allHandlers := append(c.middlewares, handlers...) | ||||
| 	if err := c.router["DELETE"].AddRouter(url, allHandlers); err != nil { | ||||
| 		log.Println(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Use registers middlewares | ||||
| func (c *Core) Use(middlewares ...ControllerHandler) { | ||||
| 	c.middlewares = append(c.middlewares, middlewares...) | ||||
| } | ||||
|  | ||||
| // FindRouteByRequest finds route using the request | ||||
| func (c *Core) FindRouteByRequest(r *http.Request) *node { | ||||
| 	upperMethod := strings.ToUpper(r.Method) | ||||
|  | ||||
| 	mapper, ok := c.router[upperMethod] | ||||
| 	if !ok { | ||||
| 		log.Printf("Method %q is not recognized\n", upperMethod) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	node := mapper.FindRoute(r.URL.Path) | ||||
| 	if node == nil { | ||||
| 		log.Printf("URI %q is not recognized\n", r.URL.Path) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return node | ||||
| } | ||||
|  | ||||
| func (c *Core) Group(prefix string) IGroup { | ||||
| 	return &Group{ | ||||
| 		core:   c, | ||||
| 		prefix: prefix, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ServeHTTP implements the Handler interface | ||||
| func (c *Core) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	log.Println("Welcome to the Araneae framework") | ||||
|  | ||||
| 	ctx := NewContext(w, r) | ||||
|  | ||||
| 	node := c.FindRouteByRequest(r) | ||||
| 	if node == nil { | ||||
| 		ctx.WriteJSON(http.StatusNotFound, "Request not found") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	params := node.ParseParamsFromEndNode(r.URL.Path) | ||||
|  | ||||
| 	ctx.SetParams(params) | ||||
| 	ctx.SetHandlers(node.handlers) | ||||
|  | ||||
| 	if err := ctx.Next(); err != nil { | ||||
| 		ctx.WriteJSON(http.StatusInternalServerError, "Internal error") | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								framework/gin
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								framework/gin
									
									
									
									
									
										Submodule
									
								
							 Submodule framework/gin added at f05f966a08
									
								
							| @ -1,54 +0,0 @@ | ||||
| package framework | ||||
|  | ||||
| // IGroup prefix routes | ||||
| type IGroup interface { | ||||
| 	Get(string, ...ControllerHandler) | ||||
| 	Post(string, ...ControllerHandler) | ||||
| 	Put(string, ...ControllerHandler) | ||||
| 	Delete(string, ...ControllerHandler) | ||||
| 	Use(...ControllerHandler) | ||||
| } | ||||
|  | ||||
| // Group is the implementation of IGroup interface | ||||
| type Group struct { | ||||
| 	core        *Core | ||||
| 	prefix      string | ||||
| 	middlewares []ControllerHandler | ||||
| } | ||||
|  | ||||
| // NewGroup create a new prefix group | ||||
| func NewGroup(core *Core, prefix string) *Group { | ||||
| 	return &Group{ | ||||
| 		core:   core, | ||||
| 		prefix: prefix, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Get is a simple get router of the group | ||||
| func (g *Group) Get(url string, handlers ...ControllerHandler) { | ||||
| 	allHandlers := append(g.middlewares, handlers...) | ||||
| 	g.core.Get(g.prefix+url, allHandlers...) | ||||
| } | ||||
|  | ||||
| // Post is a simple post router of the group | ||||
| func (g *Group) Post(url string, handlers ...ControllerHandler) { | ||||
| 	allHandlers := append(g.middlewares, handlers...) | ||||
| 	g.core.Post(g.prefix+url, allHandlers...) | ||||
| } | ||||
|  | ||||
| // Put is a simple put router of the group | ||||
| func (g *Group) Put(url string, handlers ...ControllerHandler) { | ||||
| 	allHandlers := append(g.middlewares, handlers...) | ||||
| 	g.core.Put(g.prefix+url, allHandlers...) | ||||
| } | ||||
|  | ||||
| // Delete is a simple delete router of the group | ||||
| func (g *Group) Delete(url string, handlers ...ControllerHandler) { | ||||
| 	allHandlers := append(g.middlewares, handlers...) | ||||
| 	g.core.Delete(g.prefix+url, allHandlers...) | ||||
| } | ||||
|  | ||||
| // Use registers middlewares | ||||
| func (g *Group) Use(middlewares ...ControllerHandler) { | ||||
| 	g.middlewares = append(g.middlewares, middlewares...) | ||||
| } | ||||
| @ -3,19 +3,18 @@ package middleware | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/go-web/framework" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| // Recovery is a middleware that recovers from the panic | ||||
| func Recovery() framework.ControllerHandler { | ||||
| 	return func(c *framework.Context) error { | ||||
| func Recovery() gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		defer func() { | ||||
| 			if err := recover(); err != nil { | ||||
| 				c.WriteJSON(http.StatusInternalServerError, err) | ||||
| 				c.JSON(http.StatusInternalServerError, err) | ||||
| 			} | ||||
| 		}() | ||||
|  | ||||
| 		c.Next() | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -3,32 +3,29 @@ package middleware | ||||
| import ( | ||||
| 	"log" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/go-web/framework" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func Test1() framework.ControllerHandler { | ||||
| 	return func(c *framework.Context) error { | ||||
| func Test1() gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		log.Println("middleware test1 pre") | ||||
| 		c.Next() | ||||
| 		log.Println("middleware test1 post") | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Test2() framework.ControllerHandler { | ||||
| 	return func(c *framework.Context) error { | ||||
| func Test2() gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		log.Println("middleware test2 pre") | ||||
| 		c.Next() | ||||
| 		log.Println("middleware test2 post") | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Test3() framework.ControllerHandler { | ||||
| 	return func(c *framework.Context) error { | ||||
| func Test3() gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		log.Println("middleware test3 pre") | ||||
| 		c.Next() | ||||
| 		log.Println("middleware test3 post") | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -6,15 +6,15 @@ import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/go-web/framework" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func Timeout(d time.Duration) framework.ControllerHandler { | ||||
| 	return func(c *framework.Context) error { | ||||
| func Timeout(d time.Duration) gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		finish := make(chan struct{}, 1) | ||||
| 		panicChan := make(chan interface{}, 1) | ||||
|  | ||||
| 		durationCtx, cancel := context.WithTimeout(c.BaseContext(), d) | ||||
| 		durationCtx, cancel := context.WithTimeout(c.Request.Context(), d) | ||||
| 		defer cancel() | ||||
|  | ||||
| 		go func() { | ||||
| @ -35,15 +35,12 @@ func Timeout(d time.Duration) framework.ControllerHandler { | ||||
| 		case p := <-panicChan: | ||||
| 			// panic | ||||
| 			log.Println(p) | ||||
| 			c.GetResponseWriter().WriteHeader(http.StatusInternalServerError) | ||||
| 			c.Status(http.StatusInternalServerError) | ||||
| 		case <-finish: | ||||
| 			// finish normally | ||||
| 			log.Println("finish") | ||||
| 		case <-durationCtx.Done(): | ||||
| 			c.SetHasTimeout() | ||||
| 			c.GetResponseWriter().WriteHeader(http.StatusRequestTimeout) | ||||
| 			c.GetResponseWriter().Write([]byte("time out")) | ||||
| 			c.JSON(http.StatusRequestTimeout, "time out") | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -8,30 +8,28 @@ import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/go-web/framework" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func TestTimeout(t *testing.T) { | ||||
| 	t.Run("Test timeout handler", func(t *testing.T) { | ||||
| 		timeoutHandler := Timeout(1 * time.Millisecond) | ||||
|  | ||||
| 		longHandler := func(c *framework.Context) error { | ||||
| 		longHandler := func(c *gin.Context) { | ||||
| 			time.Sleep(2 * time.Millisecond) | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		res := prepareMiddlewareTest(t, timeoutHandler, longHandler) | ||||
|  | ||||
| 		assertCode(t, res.StatusCode, http.StatusRequestTimeout) | ||||
| 		assertBody(t, res.Body, "time out") | ||||
| 		assertBody(t, res.Body, "\"time out\"") | ||||
| 	}) | ||||
| 	t.Run("Test no timeout", func(t *testing.T) { | ||||
| 		timeoutHandler := Timeout(2 * time.Millisecond) | ||||
|  | ||||
| 		quickHandler := func(c *framework.Context) error { | ||||
| 		quickHandler := func(c *gin.Context) { | ||||
| 			// time.Sleep(1 * time.Millisecond) | ||||
| 			c.WriteJSON(http.StatusOK, "ok") | ||||
| 			return nil | ||||
| 			c.JSON(http.StatusOK, "ok") | ||||
| 		} | ||||
|  | ||||
| 		res := prepareMiddlewareTest(t, timeoutHandler, quickHandler) | ||||
| @ -44,7 +42,7 @@ func TestRecover(t *testing.T) { | ||||
| 	t.Run("Test panic", func(t *testing.T) { | ||||
| 		recoverer := Recovery() | ||||
|  | ||||
| 		panicHandler := func(c *framework.Context) error { | ||||
| 		panicHandler := func(c *gin.Context) { | ||||
| 			panic("panic") | ||||
| 		} | ||||
|  | ||||
| @ -55,9 +53,8 @@ func TestRecover(t *testing.T) { | ||||
| 	t.Run("Test no panic", func(t *testing.T) { | ||||
| 		recoverer := Recovery() | ||||
|  | ||||
| 		normalHandler := func(c *framework.Context) error { | ||||
| 			c.WriteJSON(http.StatusOK, "ok") | ||||
| 			return nil | ||||
| 		normalHandler := func(c *gin.Context) { | ||||
| 			c.JSON(http.StatusOK, "ok") | ||||
| 		} | ||||
|  | ||||
| 		res := prepareMiddlewareTest(t, recoverer, normalHandler) | ||||
| @ -68,20 +65,18 @@ func TestRecover(t *testing.T) { | ||||
|  | ||||
| func prepareMiddlewareTest( | ||||
| 	t testing.TB, | ||||
| 	mid framework.ControllerHandler, | ||||
| 	in framework.ControllerHandler, | ||||
| 	mid gin.HandlerFunc, | ||||
| 	in gin.HandlerFunc, | ||||
| ) *http.Response { | ||||
| 	t.Helper() | ||||
| 	request := httptest.NewRequest(http.MethodGet, "/", nil) | ||||
| 	response := httptest.NewRecorder() | ||||
| 	c := framework.NewContext(response, request) | ||||
| 	_, r := gin.CreateTestContext(response) | ||||
|  | ||||
| 	c.SetHandlers([]framework.ControllerHandler{in}) | ||||
| 	r.Use(mid) | ||||
| 	r.GET("/", in) | ||||
|  | ||||
| 	err := mid(c) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	r.ServeHTTP(response, request) | ||||
|  | ||||
| 	res := response.Result() | ||||
| 	return res | ||||
|  | ||||
| @ -1,411 +0,0 @@ | ||||
| package framework | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"encoding/xml" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"mime/multipart" | ||||
| 	"net/url" | ||||
|  | ||||
| 	"github.com/spf13/cast" | ||||
| ) | ||||
|  | ||||
| type IRequest interface { | ||||
| 	// url query | ||||
| 	// e.g. foo.com?a=1&b=bar&c[]=bar | ||||
| 	QueryAll(key string) url.Values | ||||
| 	QueryInt(key string, defval int) (int, bool) | ||||
| 	QueryInt64(key string, defval int64) (int64, bool) | ||||
| 	QueryFloat32(key string, defval float32) (float32, bool) | ||||
| 	QueryFloat64(key string, defval float64) (float64, bool) | ||||
| 	QueryBool(key string, defval bool) (bool, bool) | ||||
| 	QueryString(key string, defval string) (string, bool) | ||||
| 	QueryStringSlice(key string, defval []string) ([]string, bool) | ||||
|  | ||||
| 	// url params | ||||
| 	// e.g. /book/:id | ||||
| 	Param(key string) any | ||||
| 	ParamInt(key string, defval int) (int, bool) | ||||
| 	ParamInt64(key string, defval int64) (int64, bool) | ||||
| 	ParamFloat32(key string, defval float32) (float32, bool) | ||||
| 	ParamFloat64(key string, defval float64) (float64, bool) | ||||
| 	ParamBool(key string, defval bool) (bool, bool) | ||||
| 	ParamString(key string, defval string) (string, bool) | ||||
|  | ||||
| 	// form | ||||
| 	FormAll(key string) url.Values | ||||
| 	FormInt(key string, defval int) (int, bool) | ||||
| 	FormInt64(key string, defval int64) (int64, bool) | ||||
| 	FormFloat32(key string, defval float32) (float32, bool) | ||||
| 	FormFloat64(key string, defval float64) (float64, bool) | ||||
| 	FormBool(key string, defval bool) (bool, bool) | ||||
| 	FormString(key string, defval string) (string, bool) | ||||
| 	FormStringSlice(key string, defval []string) ([]string, bool) | ||||
| 	FormFile(key string) (*multipart.FileHeader, error) | ||||
|  | ||||
| 	// JSON body | ||||
| 	BindJSON(obj any) error | ||||
|  | ||||
| 	// XML body | ||||
| 	BindXML(obj any) error | ||||
|  | ||||
| 	// RAW body | ||||
| 	GetRawData() ([]byte, error) | ||||
|  | ||||
| 	// Basic informations | ||||
| 	Uri() string | ||||
| 	Method() string | ||||
| 	Host() string | ||||
| 	ClientIP() string | ||||
|  | ||||
| 	// Header | ||||
| 	Headers() map[string][]string | ||||
| 	Header(key string) (string, bool) | ||||
|  | ||||
| 	// Cookie | ||||
| 	Cookies() map[string]string | ||||
| 	Cookie(key string) (string, bool) | ||||
| } | ||||
|  | ||||
| // {{{ url query | ||||
|  | ||||
| // QueryAll returns all queries in a request URL | ||||
| func (ctx *Context) QueryAll() url.Values { | ||||
| 	if ctx.request != nil { | ||||
| 		return map[string][]string(ctx.request.URL.Query()) | ||||
| 	} | ||||
| 	return url.Values{} | ||||
| } | ||||
|  | ||||
| // QueryInt gets an int value from the query request | ||||
| func (ctx *Context) QueryInt(key string, defval int) (int, bool) { | ||||
| 	params := ctx.QueryAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToInt(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) QueryInt64(key string, defval int64) (int64, bool) { | ||||
| 	params := ctx.QueryAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToInt64(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) QueryBool(key string, defval bool) (bool, bool) { | ||||
| 	params := ctx.QueryAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToBool(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) QueryFloat32(key string, defval float32) (float32, bool) { | ||||
| 	params := ctx.QueryAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToFloat32(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) QueryFloat64(key string, defval float64) (float64, bool) { | ||||
| 	params := ctx.QueryAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToFloat64(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| // QueryString gets a string value from the query request | ||||
| func (ctx *Context) QueryString(key string, defval string) (string, bool) { | ||||
| 	params := ctx.QueryAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToString(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| // QueryArray gets an array of string values from the query request | ||||
| func (ctx *Context) QueryStringSlice(key string, defval []string) ([]string, bool) { | ||||
| 	params := ctx.QueryAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		return cast.ToStringSlice(vals[0]), true | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| // {{{ url params | ||||
|  | ||||
| func (ctx *Context) Param(key string) any { | ||||
| 	if ctx.params != nil { | ||||
| 		if val, ok := ctx.params[key]; ok { | ||||
| 			return val | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (ctx *Context) ParamInt(key string, def int) (int, bool) { | ||||
| 	if val := ctx.Param(key); val != nil { | ||||
| 		return cast.ToInt(val), true | ||||
| 	} | ||||
| 	return def, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) ParamInt64(key string, def int64) (int64, bool) { | ||||
| 	if val := ctx.Param(key); val != nil { | ||||
| 		return cast.ToInt64(val), true | ||||
| 	} | ||||
| 	return def, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) ParamFloat64(key string, def float64) (float64, bool) { | ||||
| 	if val := ctx.Param(key); val != nil { | ||||
| 		return cast.ToFloat64(val), true | ||||
| 	} | ||||
| 	return def, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) ParamFloat32(key string, def float32) (float32, bool) { | ||||
| 	if val := ctx.Param(key); val != nil { | ||||
| 		return cast.ToFloat32(val), true | ||||
| 	} | ||||
| 	return def, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) ParamBool(key string, def bool) (bool, bool) { | ||||
| 	if val := ctx.Param(key); val != nil { | ||||
| 		return cast.ToBool(val), true | ||||
| 	} | ||||
| 	return def, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) ParamString(key string, def string) (string, bool) { | ||||
| 	if val := ctx.Param(key); val != nil { | ||||
| 		return cast.ToString(val), true | ||||
| 	} | ||||
| 	return def, false | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| // {{{ Post form | ||||
|  | ||||
| // FormAll gets everything from the submitted form | ||||
| func (ctx *Context) FormAll() url.Values { | ||||
| 	if ctx.request != nil { | ||||
| 		_ = ctx.request.ParseForm() | ||||
| 		return ctx.request.PostForm | ||||
| 	} | ||||
| 	return url.Values{} | ||||
| } | ||||
|  | ||||
| // FormInt gets an int value from the submitted form | ||||
| func (ctx *Context) FormInt(key string, defval int) (int, bool) { | ||||
| 	params := ctx.FormAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToInt(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) FormInt64(key string, defval int64) (int64, bool) { | ||||
| 	params := ctx.FormAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToInt64(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) FormBool(key string, defval bool) (bool, bool) { | ||||
| 	params := ctx.FormAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToBool(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) FormFloat32(key string, defval float32) (float32, bool) { | ||||
| 	params := ctx.FormAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToFloat32(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) FormFloat64(key string, defval float64) (float64, bool) { | ||||
| 	params := ctx.FormAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToFloat64(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) FormString(key string, defval string) (string, bool) { | ||||
| 	params := ctx.FormAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		if len(vals) > 0 { | ||||
| 			return cast.ToString(vals[0]), true | ||||
| 		} | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| func (ctx *Context) FormStringSlice(key string, defval []string) ([]string, bool) { | ||||
| 	params := ctx.FormAll() | ||||
| 	if vals, ok := params[key]; ok { | ||||
| 		return cast.ToStringSlice(vals[0]), true | ||||
| 	} | ||||
| 	return defval, false | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| // {{{ type binder | ||||
|  | ||||
| var ( | ||||
| 	ErrNoRequest    = errors.New("missing request in the context") | ||||
| 	ErrNotSingleObj = errors.New("body must have only a single value") | ||||
| ) | ||||
|  | ||||
| // JSON body | ||||
| func (ctx *Context) BindJSON(obj any) error { | ||||
| 	if ctx.request == nil { | ||||
| 		return ErrNoRequest | ||||
| 	} | ||||
| 	dec := json.NewDecoder(ctx.request.Body) | ||||
| 	err := dec.Decode(obj) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = dec.Decode(&struct{}{}) | ||||
| 	if err != io.EOF { | ||||
| 		return ErrNotSingleObj | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // XML body | ||||
| func (ctx *Context) BindXML(obj any) error { | ||||
| 	if ctx.request == nil { | ||||
| 		return ErrNoRequest | ||||
| 	} | ||||
| 	dec := xml.NewDecoder(ctx.request.Body) | ||||
| 	err := dec.Decode(obj) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = dec.Decode(&struct{}{}) | ||||
| 	if err != io.EOF { | ||||
| 		return ErrNotSingleObj | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RAW body | ||||
| func (ctx *Context) GetRawData() ([]byte, error) { | ||||
| 	if ctx.request == nil { | ||||
| 		return []byte{}, ErrNoRequest | ||||
| 	} | ||||
| 	body, err := io.ReadAll(ctx.request.Body) | ||||
| 	if err != nil { | ||||
| 		return []byte{}, err | ||||
| 	} | ||||
| 	/* Restore the body (io.ReadCloser) to it's original state */ | ||||
| 	ctx.request.Body = io.NopCloser(bytes.NewBuffer(body)) | ||||
|  | ||||
| 	return body, nil | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| // {{{ Basic informations | ||||
|  | ||||
| func (ctx *Context) Uri() string { | ||||
| 	return ctx.request.RequestURI | ||||
| } | ||||
|  | ||||
| func (ctx *Context) Method() string { | ||||
| 	return ctx.request.Method | ||||
| } | ||||
|  | ||||
| func (ctx *Context) Host() string { | ||||
| 	return ctx.request.Host | ||||
| } | ||||
|  | ||||
| func (ctx *Context) ClientIP() string { | ||||
| 	r := ctx.request | ||||
| 	ipAddress := r.Header.Get("X-Real-Ip") | ||||
| 	if ipAddress == "" { | ||||
| 		ipAddress = r.Header.Get("X-Forwarded-For") | ||||
| 	} | ||||
| 	if ipAddress == "" { | ||||
| 		ipAddress = r.RemoteAddr | ||||
| 	} | ||||
| 	return ipAddress | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| // {{{ Headers | ||||
|  | ||||
| // Header | ||||
| func (ctx *Context) Headers() map[string][]string { | ||||
| 	return ctx.request.Header | ||||
| } | ||||
|  | ||||
| func (ctx *Context) Header(key string) (string, bool) { | ||||
| 	vals := ctx.request.Header.Values(key) | ||||
| 	if vals == nil || len(vals) <= 0 { | ||||
| 		return "", false | ||||
| 	} | ||||
| 	return vals[0], true | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| // {{{ Cookies | ||||
|  | ||||
| // Cookies gets cookie key-value pairs | ||||
| func (ctx *Context) Cookies() map[string]string { | ||||
| 	cookies := ctx.request.Cookies() | ||||
| 	ret := map[string]string{} | ||||
| 	for _, c := range cookies { | ||||
| 		ret[c.Name] = c.Value | ||||
| 	} | ||||
| 	return ret | ||||
| } | ||||
|  | ||||
| func (ctx *Context) Cookie(key string) (string, bool) { | ||||
| 	cookies := ctx.Cookies() | ||||
| 	if val, ok := cookies[key]; ok { | ||||
| 		return val, true | ||||
| 	} | ||||
| 	return "", false | ||||
| } | ||||
|  | ||||
| // }}} | ||||
| @ -1,123 +0,0 @@ | ||||
| package framework | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"encoding/xml" | ||||
| 	"fmt" | ||||
| 	"html/template" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| ) | ||||
|  | ||||
| type IResponse interface { | ||||
| 	WriteJSON(status int, obj any) IResponse | ||||
| 	WriteXML(status int, obj any) IResponse | ||||
| 	WriteHTML(status int, filepath string, obj any) IResponse | ||||
| 	WriteText(format string, values ...any) IResponse | ||||
|  | ||||
| 	Redirect(path string) IResponse | ||||
| 	SetHeader(key string, val string) IResponse | ||||
| 	SetCookie( | ||||
| 		key string, | ||||
| 		val string, | ||||
| 		maxAge int, | ||||
| 		path, domain string, | ||||
| 		secure, httpOnly bool, | ||||
| 	) IResponse | ||||
| 	SetStatus(code int) IResponse | ||||
|  | ||||
| 	// set 200 | ||||
| 	SetOkStatus() IResponse | ||||
| } | ||||
|  | ||||
| func (ctx *Context) WriteJSON(status int, obj any) IResponse { | ||||
| 	// There is a timeout, some error message data must have already been | ||||
| 	// written to the output. Stop writing anything into the responseWriter. | ||||
| 	if ctx.HasTimeout() { | ||||
| 		return nil | ||||
| 	} | ||||
| 	data, err := json.Marshal(obj) | ||||
| 	if err != nil { | ||||
| 		return ctx.SetStatus(http.StatusInternalServerError) | ||||
| 	} | ||||
| 	ctx.responseWriter.Header().Set("Content-type", "application/json") | ||||
| 	ctx.responseWriter.WriteHeader(status) | ||||
| 	_, _ = ctx.responseWriter.Write(data) | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
| func (ctx *Context) WriteXML(status int, obj any) IResponse { | ||||
| 	// There is a timeout, some error message data must have already been | ||||
| 	// written to the output. Stop writing anything into the responseWriter. | ||||
| 	if ctx.HasTimeout() { | ||||
| 		return nil | ||||
| 	} | ||||
| 	data, err := xml.Marshal(obj) | ||||
| 	if err != nil { | ||||
| 		return ctx.SetStatus(http.StatusInternalServerError) | ||||
| 	} | ||||
| 	ctx.responseWriter.Header().Set("Content-type", "application/json") | ||||
| 	ctx.responseWriter.WriteHeader(status) | ||||
| 	_, _ = ctx.responseWriter.Write(data) | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
| func (ctx *Context) WriteHTML(status int, filepath string, obj any) IResponse { | ||||
| 	ctx.SetHeader("Content-Type", "application/html") | ||||
| 	t, _ := template.New("output").ParseFiles(filepath) | ||||
| 	t.Execute(ctx.responseWriter, obj) | ||||
|  | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
| func (ctx *Context) WriteText(format string, values ...any) IResponse { | ||||
| 	out := fmt.Sprintf(format, values...) | ||||
| 	ctx.SetHeader("Content-Type", "application/text") | ||||
| 	ctx.responseWriter.Write([]byte(out)) | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
| func (ctx *Context) Redirect(path string) IResponse { | ||||
| 	http.Redirect(ctx.responseWriter, ctx.request, path, http.StatusTemporaryRedirect) | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
| func (ctx *Context) SetHeader(key string, val string) IResponse { | ||||
| 	ctx.responseWriter.Header().Add(key, val) | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
| func (ctx *Context) SetCookie( | ||||
| 	key string, | ||||
| 	val string, | ||||
| 	maxAge int, | ||||
| 	path, domain string, | ||||
| 	secure, httpOnly bool, | ||||
| ) IResponse { | ||||
| 	if path == "" { | ||||
| 		path = "/" | ||||
| 	} | ||||
|  | ||||
| 	http.SetCookie(ctx.responseWriter, &http.Cookie{ | ||||
| 		Name:     key, | ||||
| 		Value:    url.QueryEscape(val), | ||||
| 		MaxAge:   maxAge, | ||||
| 		Path:     path, | ||||
| 		Domain:   domain, | ||||
| 		SameSite: http.SameSiteDefaultMode, | ||||
| 		Secure:   secure, | ||||
| 		HttpOnly: httpOnly, | ||||
| 	}) | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
| func (ctx *Context) SetStatus(code int) IResponse { | ||||
| 	ctx.responseWriter.WriteHeader(code) | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
| // set 200 | ||||
| func (ctx *Context) SetOkStatus() IResponse { | ||||
| 	ctx.responseWriter.WriteHeader(http.StatusOK) | ||||
| 	return ctx | ||||
| } | ||||
| @ -1,180 +0,0 @@ | ||||
| package framework | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| type Trie struct { | ||||
| 	root *node | ||||
| } | ||||
|  | ||||
| func NewTrie() *Trie { | ||||
| 	return &Trie{root: newNode("")} | ||||
| } | ||||
|  | ||||
| func (t *Trie) FindRoute(uri string) *node { | ||||
| 	uri = strings.ToUpper(uri) | ||||
| 	uri = strings.TrimPrefix(uri, "/") | ||||
| 	if uri == "" { | ||||
| 		return t.root | ||||
| 	} | ||||
|  | ||||
| 	found := t.root.findRoute(uri) | ||||
| 	if found == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return found | ||||
| } | ||||
|  | ||||
| func (t *Trie) AddRouter(uri string, handlers []ControllerHandler) error { | ||||
| 	uri = strings.ToUpper(uri) | ||||
| 	uri = strings.TrimPrefix(uri, "/") | ||||
| 	if uri == "" { | ||||
| 		t.root.isLast = true | ||||
| 		t.root.handlers = append(t.root.handlers, handlers...) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	upperUri := strings.ToUpper(uri) | ||||
| 	match := t.FindRoute(upperUri) | ||||
| 	if match != nil { | ||||
| 		// existing route | ||||
| 		return fmt.Errorf("existing route for %q", uri) | ||||
| 	} | ||||
|  | ||||
| 	// The route does not exist, add it to the tree | ||||
| 	err := t.root.addRoute(upperUri, handlers) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type node struct { | ||||
| 	isLast   bool | ||||
| 	segment  string | ||||
| 	handlers []ControllerHandler | ||||
| 	children []*node | ||||
| 	parent   *node | ||||
| } | ||||
|  | ||||
| func (n *node) ParseParamsFromEndNode(uri string) map[string]string { | ||||
| 	ret := map[string]string{} | ||||
| 	uri = strings.ToUpper(uri) | ||||
| 	uri = strings.TrimPrefix(uri, "/") | ||||
| 	if uri == "" { | ||||
| 		// root | ||||
| 		return ret | ||||
| 	} | ||||
| 	segments := strings.Split(uri, "/") | ||||
| 	cnt := len(segments) | ||||
| 	cur := n | ||||
|  | ||||
| 	for i := cnt - 1; i >= 0; i-- { | ||||
| 		if isWildcard(cur.segment) { | ||||
| 			// set params | ||||
| 			ret[cur.segment[1:]] = segments[i] | ||||
| 		} | ||||
| 		cur = cur.parent | ||||
| 	} | ||||
| 	return ret | ||||
| } | ||||
|  | ||||
| func newNode(segment string) *node { | ||||
| 	return &node{segment: segment} | ||||
| } | ||||
|  | ||||
| func isWildcard(s string) bool { | ||||
| 	return strings.HasPrefix(s, ":") | ||||
| } | ||||
|  | ||||
| // /user/name | ||||
| // /user/:id/name | ||||
| // /user/3/name | ||||
|  | ||||
| // findRoute finds the handler for the uri if exists. | ||||
| // | ||||
| // we suppose that uri passed here doesn't begin with "/" | ||||
| func (n *node) findRoute(uri string) *node { | ||||
| 	splitted := strings.SplitN(uri, "/", 2) | ||||
| 	splittedLen := len(splitted) | ||||
|  | ||||
| 	if isWildcard(splitted[0]) { | ||||
| 		// input is a wildcard, check if this endpoint has already children | ||||
| 		// if so, return the first one which is not nil. | ||||
| 		nbChildren := len(n.children) | ||||
| 		if nbChildren > 0 && n.children[0].segment != splitted[0] { | ||||
| 			// several nodes exist, return the first one that is not nil | ||||
| 			return n.children[0] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// try to find the value in childre | ||||
| 	for _, child := range n.children { | ||||
| 		if isWildcard(child.segment) || child.segment == splitted[0] { | ||||
| 			if splittedLen == 1 { | ||||
| 				// This is the last value, do the check and return | ||||
| 				if child.isLast { | ||||
| 					// if isLast, that means we have already registered the endpoint | ||||
| 					return child | ||||
| 				} else { | ||||
| 					// otherwise, take it as not registered | ||||
| 					return nil | ||||
| 				} | ||||
| 			} | ||||
| 			// More segments to check | ||||
| 			return child.findRoute(splitted[1]) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// nothing found in the children | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (n *node) addRoute(uri string, handlers []ControllerHandler) error { | ||||
| 	splitted := strings.SplitN(uri, "/", 2) | ||||
| 	splittedLen := len(splitted) | ||||
| 	isLast := splittedLen == 1 | ||||
|  | ||||
| 	// try to find the value in childre | ||||
| 	for _, child := range n.children { | ||||
| 		if isWildcard(child.segment) || child.segment == splitted[0] { | ||||
| 			if isLast { | ||||
| 				// This is the last value, do the check and return | ||||
| 				if child.isLast { | ||||
| 					// if isLast, that means we have already registered the endpoint | ||||
| 					return errors.New("node exists") | ||||
| 				} else { | ||||
| 					// otherwise, set the child | ||||
| 					child.isLast = true | ||||
| 					child.handlers = append(child.handlers, handlers...) | ||||
| 					return nil | ||||
| 				} | ||||
| 			} | ||||
| 			// More segments to check | ||||
| 			return child.addRoute(splitted[1], handlers) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// create a new node | ||||
| 	new := newNode(splitted[0]) | ||||
| 	new.parent = n | ||||
| 	if isLast { | ||||
| 		// this is the end | ||||
| 		new.handlers = append(new.handlers, handlers...) | ||||
| 		new.isLast = true | ||||
| 		n.children = append(n.children, new) | ||||
| 		return nil | ||||
| 	} | ||||
| 	// continue | ||||
| 	new.isLast = false | ||||
| 	err := new.addRoute(splitted[1], handlers) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	n.children = append(n.children, new) | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										44
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								go.mod
									
									
									
									
									
								
							| @ -2,4 +2,46 @@ module git.vinchent.xyz/vinchent/go-web | ||||
|  | ||||
| go 1.22.5 | ||||
|  | ||||
| require github.com/spf13/cast v1.7.0 | ||||
| require github.com/gin-gonic/gin v1.10.0 | ||||
|  | ||||
| require ( | ||||
| 	github.com/bytedance/sonic v1.11.6 // indirect | ||||
| 	github.com/bytedance/sonic/loader v0.1.1 // indirect | ||||
| 	github.com/cloudwego/base64x v0.1.4 // indirect | ||||
| 	github.com/cloudwego/iasm v0.2.0 // indirect | ||||
| 	github.com/gabriel-vasile/mimetype v1.4.3 // indirect | ||||
| 	github.com/gin-contrib/sse v0.1.0 // indirect | ||||
| 	github.com/go-playground/locales v0.14.1 // indirect | ||||
| 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||
| 	github.com/go-playground/validator/v10 v10.20.0 // indirect | ||||
| 	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect | ||||
| 	github.com/goccy/go-json v0.10.2 // indirect | ||||
| 	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect | ||||
| 	github.com/json-iterator/go v1.1.12 // indirect | ||||
| 	github.com/klauspost/cpuid/v2 v2.2.7 // indirect | ||||
| 	github.com/kr/text v0.2.0 // indirect | ||||
| 	github.com/leodido/go-urn v1.4.0 // indirect | ||||
| 	github.com/mattn/go-isatty v0.0.20 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/onsi/ginkgo/v2 v2.9.5 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.2 // indirect | ||||
| 	github.com/quic-go/qpack v0.4.0 // indirect | ||||
| 	github.com/quic-go/quic-go v0.43.1 // indirect | ||||
| 	github.com/rogpeppe/go-internal v1.9.0 // indirect | ||||
| 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||||
| 	github.com/ugorji/go/codec v1.2.12 // indirect | ||||
| 	go.uber.org/mock v0.4.0 // indirect | ||||
| 	golang.org/x/arch v0.8.0 // indirect | ||||
| 	golang.org/x/crypto v0.25.0 // indirect | ||||
| 	golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect | ||||
| 	golang.org/x/mod v0.17.0 // indirect | ||||
| 	golang.org/x/net v0.27.0 // indirect | ||||
| 	golang.org/x/sys v0.22.0 // indirect | ||||
| 	golang.org/x/text v0.16.0 // indirect | ||||
| 	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect | ||||
| 	google.golang.org/protobuf v1.34.1 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|  | ||||
| replace github.com/gin-gonic/gin => ./framework/gin | ||||
|  | ||||
							
								
								
									
										127
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								go.sum
									
									
									
									
									
								
							| @ -1,12 +1,127 @@ | ||||
| github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | ||||
| github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= | ||||
| github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= | ||||
| github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= | ||||
| github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= | ||||
| github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | ||||
| github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | ||||
| github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | ||||
| github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= | ||||
| github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= | ||||
| github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= | ||||
| github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= | ||||
| 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/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= | ||||
| github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= | ||||
| github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= | ||||
| github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= | ||||
| github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= | ||||
| github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= | ||||
| github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= | ||||
| github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= | ||||
| github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | ||||
| github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= | ||||
| github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= | ||||
| github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= | ||||
| github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= | ||||
| github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= | ||||
| github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= | ||||
| github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= | ||||
| github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= | ||||
| github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= | ||||
| github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | ||||
| github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||||
| github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= | ||||
| github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||
| github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | ||||
| github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= | ||||
| github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | ||||
| github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= | ||||
| github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||||
| github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= | ||||
| github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= | ||||
| github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||||
| github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | ||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||||
| github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= | ||||
| github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= | ||||
| github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= | ||||
| github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= | ||||
| github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= | ||||
| github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= | ||||
| 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/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= | ||||
| github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= | ||||
| github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= | ||||
| github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= | ||||
| github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= | ||||
| github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= | ||||
| github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= | ||||
| github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= | ||||
| 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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||||
| github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||
| github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= | ||||
| github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | ||||
| github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= | ||||
| github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= | ||||
| go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= | ||||
| go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= | ||||
| golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= | ||||
| golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= | ||||
| golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||
| golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= | ||||
| golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= | ||||
| golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= | ||||
| golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= | ||||
| golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= | ||||
| golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | ||||
| golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= | ||||
| golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= | ||||
| golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= | ||||
| golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||||
| golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= | ||||
| golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= | ||||
| golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= | ||||
| golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= | ||||
| golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= | ||||
| golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= | ||||
| golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= | ||||
| google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= | ||||
| google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= | ||||
| rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= | ||||
|  | ||||
							
								
								
									
										78
									
								
								handlers.go
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								handlers.go
									
									
									
									
									
								
							| @ -1,85 +1,29 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/go-web/framework" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| // func FooControllerHandler(ctx *framework.Context) error { | ||||
| // 	return ctx.WriteJSON(http.StatusOK, map[string]any{ | ||||
| // 		"code": 0, | ||||
| // 	}) | ||||
| // } | ||||
|  | ||||
| func FooControllerHandler(ctx *framework.Context) error { | ||||
| 	durationCtx, cancel := context.WithTimeout(ctx.BaseContext(), time.Duration(1*time.Second)) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	finish := make(chan struct{}, 1) | ||||
| 	panicChan := make(chan interface{}, 1) | ||||
|  | ||||
| 	// some long task | ||||
| 	go func() { | ||||
| 		// Deal with the panic during the work | ||||
| 		defer func() { | ||||
| 			if p := recover(); p != nil { | ||||
| 				panicChan <- p | ||||
| 			} | ||||
| 		}() | ||||
| 		// do the business | ||||
| 		time.Sleep(10 * time.Second) | ||||
| 		ctx.WriteJSON(http.StatusOK, "ok") | ||||
| 		finish <- struct{}{} | ||||
| 	}() | ||||
|  | ||||
| 	select { | ||||
| 	case <-panicChan: | ||||
| 		// Protect ResponseWriter for concurrently writing from different | ||||
| 		// goroutines if there are any | ||||
| 		ctx.WriterMux().Lock() | ||||
| 		defer ctx.WriterMux().Unlock() | ||||
| 		log.Println("panicked") | ||||
| 		ctx.WriteJSON(http.StatusInternalServerError, "panicked") | ||||
| 	case <-finish: | ||||
| 		log.Println("finished") | ||||
| 	case <-durationCtx.Done(): | ||||
| 		ctx.WriterMux().Lock() | ||||
| 		defer ctx.WriterMux().Unlock() | ||||
| 		log.Println("Timeout") | ||||
| 		ctx.WriteJSON(http.StatusInternalServerError, "time out") | ||||
| 		ctx.SetHasTimeout() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func UserLoginController(ctx *framework.Context) error { | ||||
| func UserLoginController(ctx *gin.Context) { | ||||
| 	time.Sleep(10 * time.Second) | ||||
| 	ctx.WriteJSON(http.StatusOK, "ok") | ||||
| 	return nil | ||||
| 	ctx.JSON(http.StatusOK, "ok") | ||||
| } | ||||
|  | ||||
| func SubjectDelController(ctx *framework.Context) error { | ||||
| 	ctx.WriteJSON(http.StatusAccepted, "deleted") | ||||
| 	return nil | ||||
| func SubjectDelController(ctx *gin.Context) { | ||||
| 	ctx.JSON(http.StatusAccepted, "deleted") | ||||
| } | ||||
|  | ||||
| func SubjectUpdateController(ctx *framework.Context) error { | ||||
| 	ctx.WriteJSON(http.StatusAccepted, "updated") | ||||
| 	return nil | ||||
| func SubjectUpdateController(ctx *gin.Context) { | ||||
| 	ctx.JSON(http.StatusAccepted, "updated") | ||||
| } | ||||
|  | ||||
| func SubjectGetController(ctx *framework.Context) error { | ||||
| 	ctx.WriteJSON(http.StatusAccepted, "got") | ||||
| 	log.Println(ctx.ParamInt("ID", 0)) | ||||
| 	return nil | ||||
| func SubjectGetController(ctx *gin.Context) { | ||||
| 	ctx.JSON(http.StatusAccepted, "got") | ||||
| } | ||||
|  | ||||
| func SubjectListController(ctx *framework.Context) error { | ||||
| 	ctx.WriteJSON(http.StatusAccepted, "list") | ||||
| 	return nil | ||||
| func SubjectListController(ctx *gin.Context) { | ||||
| 	ctx.JSON(http.StatusAccepted, "list") | ||||
| } | ||||
|  | ||||
							
								
								
									
										4
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.go
									
									
									
									
									
								
							| @ -10,11 +10,11 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/go-web/framework" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	core := framework.NewCore() | ||||
| 	core := gin.New() | ||||
| 	registerRouter(core) | ||||
| 	server := &http.Server{ | ||||
| 		Addr:    ":8080", | ||||
|  | ||||
							
								
								
									
										16
									
								
								routes.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								routes.go
									
									
									
									
									
								
							| @ -1,21 +1,21 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"git.vinchent.xyz/vinchent/go-web/framework" | ||||
| 	"git.vinchent.xyz/vinchent/go-web/framework/middleware" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func registerRouter(core *framework.Core) { | ||||
| func registerRouter(core *gin.Engine) { | ||||
| 	core.Use(middleware.Test1(), middleware.Test2()) | ||||
| 	core.Get("/user/login", UserLoginController) | ||||
| 	core.GET("/user/login", UserLoginController) | ||||
|  | ||||
| 	subjectApi := core.Group("/subject") | ||||
| 	{ | ||||
| 		subjectApi.Use(middleware.Test3()) | ||||
| 		subjectApi.Delete("/:id", SubjectDelController) | ||||
| 		subjectApi.Put("/:id", SubjectUpdateController) | ||||
| 		subjectApi.Get("/:id", SubjectGetController) | ||||
| 		subjectApi.Get("/:id/test", SubjectGetController) | ||||
| 		subjectApi.Get("/list/all", SubjectListController) | ||||
| 		subjectApi.DELETE("/:id", SubjectDelController) | ||||
| 		subjectApi.PUT("/:id", SubjectUpdateController) | ||||
| 		subjectApi.GET("/:id", SubjectGetController) | ||||
| 		subjectApi.GET("/:id/test", SubjectGetController) | ||||
| 		subjectApi.GET("/list/all", SubjectListController) | ||||
| 	} | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user