feat: Add request body size limit and enhance error logging

This commit introduces two main improvements to the Touka web framework:

1.  **Configurable Request Body Size Limit:**
    - Added `MaxRequestBodySize int64` to `touka.Engine` (default 10MB).
    - You can customize this via `engine.SetMaxRequestBodySize()`.
    - The context methods `GetReqBodyFull()`, `GetReqBodyBuffer()`, and `ShouldBindJSON()` now adhere to this limit. They check `Content-Length` upfront and use `http.MaxBytesReader` to prevent reading excessively large request bodies into memory, enhancing protection against potential DoS attacks or high memory usage.
    - Added comprehensive unit tests in `context_test.go` for this feature, covering scenarios where the limit is active, disabled, and exceeded.

2.  **Enhanced Error Logging in Default Handler:**
    - The `defaultErrorHandle` in `engine.go` now logs not only the primary error passed to it but also any additional errors collected in `Context.Errors` (via `c.AddError()`).
    - This provides more comprehensive diagnostic information in the logs without altering the JSON error response structure sent to the client, ensuring backward compatibility.

These changes aim to improve the framework's robustness, memory safety, and debuggability.
This commit is contained in:
google-labs-jules[bot] 2025-06-20 06:35:01 +00:00
parent 543b3165ca
commit 82099e26ee
3 changed files with 336 additions and 15 deletions

View file

@ -74,6 +74,8 @@ type Engine struct {
// 如果设置了此回调,它将优先于 ServerConfigurator 被用于 HTTPS 服务器
// 如果未设置,HTTPS 服务器将回退使用 ServerConfigurator (如果已设置)
TLSServerConfigurator func(*http.Server)
MaxRequestBodySize int64 // 限制读取Body的最大字节数
}
type ErrorHandle struct {
@ -87,12 +89,39 @@ type ErrorHandler func(c *Context, code int, err error)
func defaultErrorHandle(c *Context, code int, err error) { // 检查客户端是否已断开连接
select {
case <-c.Request.Context().Done():
// 客户端断开连接,无需进一步处理
return
default:
// 检查响应是否已经写入
if c.Writer.Written() {
return
}
// 收集错误信息用于日志记录
primaryErrStr := "none"
if err != nil {
primaryErrStr = err.Error()
}
var collectedErrors []string
for _, e := range c.GetErrors() {
collectedErrors = append(collectedErrors, e.Error())
}
collectedErrorsStr := strings.Join(collectedErrors, "; ")
if collectedErrorsStr == "" {
collectedErrorsStr = "none"
}
// 记录错误日志
logMessage := fmt.Sprintf("[Touka ErrorHandler] Request: [%s] %s | Primary Error: %s | Collected Errors: %s",
c.Request.Method, c.Request.URL.Path, primaryErrStr, collectedErrorsStr)
if c.engine != nil && c.engine.LogReco != nil {
c.engine.LogReco.Error(logMessage)
} else {
log.Println(logMessage) // Fallback to standard logger
}
// 输出json 状态码与状态码对应描述
var errMsg string
if err != nil {
@ -160,6 +189,7 @@ func New() *Engine {
noRoutes: make(HandlersChain, 0),
ServerConfigurator: nil,
TLSServerConfigurator: nil,
MaxRequestBodySize: 10 * 1024 * 1024, // 默认 10MB
}
//engine.SetProtocols(GetDefaultProtocolsConfig())
engine.SetDefaultProtocols()
@ -189,6 +219,11 @@ func Default() *Engine {
// === 外部操作方法 ===
// SetMaxRequestBodySize 设置读取Body的最大字节数
func (engine *Engine) SetMaxRequestBodySize(size int64) {
engine.MaxRequestBodySize = size
}
// SetServerConfigurator 设置一个函数,该函数将在任何 HTTP 或 HTTPS 服务器
// (通过 RunShutdown, RunTLS, RunTLSRedir) 启动前被调用,
// 允许用户对底层的 *http.Server 实例进行自定义配置