Repo: reorganize the repo structure
This commit is contained in:
		
							
								
								
									
										20
									
								
								framework/middlewares/recovery.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								framework/middlewares/recovery.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Recovery is a middleware that recovers from the panic
 | 
			
		||||
func Recovery() gin.HandlerFunc {
 | 
			
		||||
	return func(c *gin.Context) {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := recover(); err != nil {
 | 
			
		||||
				c.JSON(http.StatusInternalServerError, err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		c.Next()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								framework/middlewares/test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								framework/middlewares/test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Test1() gin.HandlerFunc {
 | 
			
		||||
	return func(c *gin.Context) {
 | 
			
		||||
		log.Println("middleware test1 pre")
 | 
			
		||||
		c.Next()
 | 
			
		||||
		log.Println("middleware test1 post")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test2() gin.HandlerFunc {
 | 
			
		||||
	return func(c *gin.Context) {
 | 
			
		||||
		log.Println("middleware test2 pre")
 | 
			
		||||
		c.Next()
 | 
			
		||||
		log.Println("middleware test2 post")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test3() gin.HandlerFunc {
 | 
			
		||||
	return func(c *gin.Context) {
 | 
			
		||||
		log.Println("middleware test3 pre")
 | 
			
		||||
		c.Next()
 | 
			
		||||
		log.Println("middleware test3 post")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								framework/middlewares/timeout.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								framework/middlewares/timeout.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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.Request.Context(), d)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		go func() {
 | 
			
		||||
			// Handle panic
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if p := recover(); p != nil {
 | 
			
		||||
					panicChan <- p
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			// Run the next middleware or the business logic
 | 
			
		||||
			c.Next()
 | 
			
		||||
 | 
			
		||||
			finish <- struct{}{}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		select {
 | 
			
		||||
		case p := <-panicChan:
 | 
			
		||||
			// panic
 | 
			
		||||
			log.Println(p)
 | 
			
		||||
			c.Status(http.StatusInternalServerError)
 | 
			
		||||
		case <-finish:
 | 
			
		||||
			// finish normally
 | 
			
		||||
			log.Println("finish")
 | 
			
		||||
		case <-durationCtx.Done():
 | 
			
		||||
			c.JSON(http.StatusRequestTimeout, "time out")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										98
									
								
								framework/middlewares/timeout_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								framework/middlewares/timeout_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,98 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"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 *gin.Context) {
 | 
			
		||||
			time.Sleep(2 * time.Millisecond)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res := prepareMiddlewareTest(t, timeoutHandler, longHandler)
 | 
			
		||||
 | 
			
		||||
		assertCode(t, res.StatusCode, http.StatusRequestTimeout)
 | 
			
		||||
		assertBody(t, res.Body, "\"time out\"")
 | 
			
		||||
	})
 | 
			
		||||
	t.Run("Test no timeout", func(t *testing.T) {
 | 
			
		||||
		timeoutHandler := Timeout(2 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
		quickHandler := func(c *gin.Context) {
 | 
			
		||||
			// time.Sleep(1 * time.Millisecond)
 | 
			
		||||
			c.JSON(http.StatusOK, "ok")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res := prepareMiddlewareTest(t, timeoutHandler, quickHandler)
 | 
			
		||||
 | 
			
		||||
		assertCode(t, res.StatusCode, http.StatusOK)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRecover(t *testing.T) {
 | 
			
		||||
	t.Run("Test panic", func(t *testing.T) {
 | 
			
		||||
		recoverer := Recovery()
 | 
			
		||||
 | 
			
		||||
		panicHandler := func(c *gin.Context) {
 | 
			
		||||
			panic("panic")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res := prepareMiddlewareTest(t, recoverer, panicHandler)
 | 
			
		||||
 | 
			
		||||
		assertCode(t, res.StatusCode, http.StatusInternalServerError)
 | 
			
		||||
	})
 | 
			
		||||
	t.Run("Test no panic", func(t *testing.T) {
 | 
			
		||||
		recoverer := Recovery()
 | 
			
		||||
 | 
			
		||||
		normalHandler := func(c *gin.Context) {
 | 
			
		||||
			c.JSON(http.StatusOK, "ok")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res := prepareMiddlewareTest(t, recoverer, normalHandler)
 | 
			
		||||
 | 
			
		||||
		assertCode(t, res.StatusCode, http.StatusOK)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func prepareMiddlewareTest(
 | 
			
		||||
	t testing.TB,
 | 
			
		||||
	mid gin.HandlerFunc,
 | 
			
		||||
	in gin.HandlerFunc,
 | 
			
		||||
) *http.Response {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
	request := httptest.NewRequest(http.MethodGet, "/", nil)
 | 
			
		||||
	response := httptest.NewRecorder()
 | 
			
		||||
	_, r := gin.CreateTestContext(response)
 | 
			
		||||
 | 
			
		||||
	r.Use(mid)
 | 
			
		||||
	r.GET("/", in)
 | 
			
		||||
 | 
			
		||||
	r.ServeHTTP(response, request)
 | 
			
		||||
 | 
			
		||||
	res := response.Result()
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func assertCode(t testing.TB, got int, want int) {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
	if got != want {
 | 
			
		||||
		t.Errorf("status code got %d, want %d", got, want)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func assertBody(t testing.TB, got io.Reader, want string) {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
	buf, _ := io.ReadAll(got)
 | 
			
		||||
	if cmp := bytes.Compare(buf, []byte(want)); cmp != 0 {
 | 
			
		||||
		t.Errorf("got %q, want %q", string(buf), want)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user