Compare commits

..

3 Commits

Author SHA1 Message Date
zhaoyupeng
b6ee2966cf fix: new cache(redis) uri invalid 2025-06-23 17:04:06 +08:00
zhaoyupeng
6d1024f951 update: cache add raw client api 2025-06-23 15:29:23 +08:00
zhaoyupeng
0e53ccc70c feat: 添加 dump request 中间件和处理程序 curl 2025-06-19 16:40:08 +08:00
6 changed files with 263 additions and 1 deletions

View File

@ -52,6 +52,9 @@ type Cache interface {
GetDel(ctx context.Context, key string) ([]byte, error) GetDel(ctx context.Context, key string) ([]byte, error)
GetDelScan(ctx context.Context, key string) Scanner GetDelScan(ctx context.Context, key string) Scanner
Close() Close()
// Client return raw client
// !!! dangerous api
Client() any
} }
var ( var (

View File

@ -37,3 +37,37 @@ func TestNew(t *testing.T) {
t.Fatal(err) t.Fatal(err)
}*/ }*/
} }
func TestNoAuth(t *testing.T) {
//if err := Init(WithRedis("10.125.1.28", 6379, "", "")); err != nil {
// t.Fatal(err)
//}
//
//type User struct {
// Name string `json:"name"`
// Age int `json:"age"`
//}
//
//if err := Default.Set(t.Context(), "zyp:haha", &User{
// Name: "cache",
// Age: 18,
//}); err != nil {
// t.Fatal(err)
//}
//
//s := Default.GetDelScan(t.Context(), "zyp:haha")
//u := new(User)
//
//if err := s.Scan(u); err != nil {
// t.Fatal(err)
//}
//
//t.Logf("%#v", *u)
//
//if err := Default.SetEx(t.Context(), "zyp:haha", &User{
// Name: "redis",
// Age: 2,
//}, time.Hour); err != nil {
// t.Fatal(err)
//}
}

View File

@ -22,7 +22,7 @@ func WithCtx(ctx context.Context) OptionFn {
func WithRedis(host string, port int, username, password string) OptionFn { func WithRedis(host string, port int, username, password string) OptionFn {
return func(c *config) { return func(c *config) {
uri := fmt.Sprintf("%s:%d", host, port) uri := fmt.Sprintf("redis://%s:%d", host, port)
if username != "" || password != "" { if username != "" || password != "" {
uri = fmt.Sprintf("redis://%s:%s@%s:%d", username, password, host, port) uri = fmt.Sprintf("redis://%s:%s@%s:%d", username, password, host, port)
} }

View File

@ -18,6 +18,10 @@ type _redis struct {
client *redis.Client client *redis.Client
} }
func (r *_redis) Client() any {
return r.client
}
func newRedis(ctx context.Context, client *redis.Client) *_redis { func newRedis(ctx context.Context, client *redis.Client) *_redis {
r := &_redis{ctx: ctx, client: client} r := &_redis{ctx: ctx, client: client}

147
middlewares/dump/req.go Normal file
View File

@ -0,0 +1,147 @@
package dump
import (
"bytes"
"context"
"gitea.loveuer.com/yizhisec/packages/logger"
"gitea.loveuer.com/yizhisec/packages/tool"
"github.com/gin-gonic/gin"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
type RequestHandler func(r *http.Request, body []byte) string
func Request(ctx context.Context, handler RequestHandler, writers ...io.Writer) gin.HandlerFunc {
var (
out io.Writer = os.Stdout
ch = make(chan string, 128)
builder = strings.Builder{}
buf = make([]string, 0, 16)
)
if len(writers) > 0 && writers[0] != nil {
out = writers[0]
}
do := func() {
for _, item := range buf {
builder.WriteString(item)
builder.WriteRune('\n')
}
_, _ = out.Write(tool.StringToBytes(builder.String()))
builder.Reset()
buf = buf[:0]
}
go func() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
if len(buf) > 0 {
do()
}
return
case <-ticker.C:
if len(buf) > 0 {
do()
}
case msg, _ := <-ch:
buf = append(buf, msg)
if len(buf) >= 10 {
do()
}
}
}
}()
return func(c *gin.Context) {
var (
err error
contentType = c.GetHeader("Content-Type")
contentLength = c.GetHeader("Content-Length")
cl int
)
if contentLength == "" && (c.Request.Method == "GET" || c.Request.Method == "HEAD") {
goto DUMP
}
if cl, err = strconv.Atoi(contentLength); err != nil {
logger.WarnCtx(c.Request.Context(), "Request: convert Content-Length failed, err = %s", err.Error())
c.Next()
return
}
if cl > 0 && !strings.Contains(contentType, "application/json") {
c.Next()
return
}
DUMP:
bodyBytes, err := io.ReadAll(c.Request.Body)
if err != nil {
logger.WarnCtx(c.Request.Context(), "读取请求体错误: %v", err)
c.Next()
return
}
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
dumped := handler(c.Request, bodyBytes)
ch <- dumped
c.Next()
}
}
func RequestHandlerCurl(r *http.Request, body []byte) string {
var builder strings.Builder
// 添加 curl 基础命令和方法
builder.WriteString("curl -X " + r.Method)
// 添加请求 URL
url := getFullURL(r)
builder.WriteString(" '" + url + "'")
// 添加请求头
for key, values := range r.Header {
if strings.EqualFold(key, "Host") {
continue // 跳过 Host 头
}
for _, value := range values {
builder.WriteString(" -H '" + key + ": " + value + "'")
}
}
// 添加 JSON 数据
if len(body) > 0 {
// 转义单引号防止命令中断
escapedBody := strings.ReplaceAll(string(body), "'", `'\''`)
builder.WriteString(" -d '" + escapedBody + "'")
}
return builder.String()
}
func getFullURL(r *http.Request) string {
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
return scheme + "://" + r.Host + r.URL.RequestURI()
}

View File

@ -0,0 +1,74 @@
package dump
import (
"bytes"
"encoding/json"
"gitea.loveuer.com/yizhisec/packages/logger"
"gitea.loveuer.com/yizhisec/packages/tool"
"github.com/gin-gonic/gin"
"net/http"
"testing"
"time"
)
func TestRequest(t *testing.T) {
ready := make(chan struct{})
go func() {
app := gin.Default()
app.Use(Request(t.Context(), RequestHandlerCurl))
app.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{"name": c.Query("name")})
})
app.POST("/hello", func(c *gin.Context) {
type Req struct {
Id int `json:"id"`
Name string `json:"name"`
}
var (
err error
req = new(Req)
)
if err = c.BindJSON(req); err != nil {
c.JSON(200, gin.H{"err": err})
}
c.JSON(200, gin.H{"id": req.Id, "name": req.Name})
})
logger.Fatal(app.Run(":18080").Error())
}()
go func() {
time.Sleep(1 * time.Second)
for _ = range 10 {
_, err := http.Get("http://localhost:18080/hello?name=" + tool.RandomName())
if err != nil {
t.Error(err.Error())
}
}
for _ = range 5 {
bs, _ := json.Marshal(map[string]interface{}{"id": tool.RandomInt(30), "name": tool.RandomName()})
req, err := http.NewRequest(http.MethodPost, "http://localhost:18080/hello", bytes.NewReader(bs))
req.Header.Set("Content-Type", "application/json")
if err != nil {
t.Fatal(err.Error())
}
_, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err.Error())
}
}
ready <- struct{}{}
}()
<-ready
time.Sleep(1 * time.Second)
}