38 lines
1.5 KiB
Go
38 lines
1.5 KiB
Go
|
|
package middleware
|
||
|
|
|
||
|
|
import (
|
||
|
|
"regexp"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
"github.com/google/uuid"
|
||
|
|
)
|
||
|
|
|
||
|
|
// requestIDKey — ключ, под которым id лежит в контексте gin.
|
||
|
|
const requestIDKey = "request_id"
|
||
|
|
|
||
|
|
// incomingID — допустимый входящий X-Request-Id: до 64 символов из
|
||
|
|
// [A-Za-z0-9._-]. Посторонние символы (переводы строк, кавычки) в логах
|
||
|
|
// выглядели бы как подделка записей, поэтому чужой заголовок отбрасываем.
|
||
|
|
var incomingID = regexp.MustCompile(`^[A-Za-z0-9._-]{1,64}$`)
|
||
|
|
|
||
|
|
// RequestID берёт id из заголовка X-Request-Id (если он осмысленный)
|
||
|
|
// или генерирует свой, кладёт в контекст и возвращает клиенту тем же
|
||
|
|
// заголовком. Дальше этот id подхватывают access-log и логи безопасности:
|
||
|
|
// один запрос проходится по логам одной строкой.
|
||
|
|
func RequestID() gin.HandlerFunc {
|
||
|
|
return func(c *gin.Context) {
|
||
|
|
id := c.GetHeader("X-Request-Id")
|
||
|
|
if !incomingID.MatchString(id) {
|
||
|
|
id = uuid.NewString()
|
||
|
|
}
|
||
|
|
c.Set(requestIDKey, id)
|
||
|
|
c.Header("X-Request-Id", id)
|
||
|
|
c.Next()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// RequestIDFrom возвращает id текущего запроса; "" вне middleware.
|
||
|
|
func RequestIDFrom(c *gin.Context) string {
|
||
|
|
return c.GetString(requestIDKey)
|
||
|
|
}
|