diff --git a/binding/binding_test.go b/binding/binding_test.go index 1044e6c2..c9dea347 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -61,6 +61,10 @@ type FooStructForMapType struct { MapFoo map[string]interface{} `form:"map_foo"` } +type FooStructForIgnoreFormTag struct { + Foo *string `form:"-"` +} + type InvalidNameType struct { TestName string `invalid_name:"test_name"` } @@ -278,6 +282,12 @@ func TestBindingFormForTime2(t *testing.T) { "", "") } +func TestFormBindingIgnoreField(t *testing.T) { + testFormBindingIgnoreField(t, "POST", + "/", "/", + "-=bar", "") +} + func TestBindingFormInvalidName(t *testing.T) { testFormBindingInvalidName(t, "POST", "/", "/", @@ -860,6 +870,21 @@ func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, bod assert.Error(t, err) } +func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBody string) { + b := Form + assert.Equal(t, "form", b.Name()) + + obj := FooStructForIgnoreFormTag{} + req := requestWithBody(method, path, body) + if method == "POST" { + req.Header.Add("Content-Type", MIMEPOSTForm) + } + err := b.Bind(req, &obj) + assert.NoError(t, err) + + assert.Nil(t, obj.Foo) +} + func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBody string) { b := Form assert.Equal(t, "form", b.Name()) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 8900ab70..8eb5c0d1 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -41,6 +41,9 @@ func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error { defaultValue = defaultList[1] } } + if inputFieldName == "-" { + continue + } if inputFieldName == "" { inputFieldName = typeField.Name diff --git a/gin.go b/gin.go index 6e5ea6d7..ad64c35f 100644 --- a/gin.go +++ b/gin.go @@ -318,6 +318,7 @@ func (engine *Engine) RunUnix(file string) (err error) { return } defer listener.Close() + os.Chmod(file, 0777) err = http.Serve(listener, engine) return } diff --git a/logger.go b/logger.go index 6d8f838e..dc639975 100644 --- a/logger.go +++ b/logger.go @@ -64,15 +64,62 @@ type LogFormatterParams struct { ErrorMessage string // IsTerm shows whether does gin's output descriptor refers to a terminal. IsTerm bool + // BodySize is the size of the Response Body + BodySize int +} + +// StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal. +func (p *LogFormatterParams) StatusCodeColor() string { + code := p.StatusCode + + switch { + case code >= http.StatusOK && code < http.StatusMultipleChoices: + return green + case code >= http.StatusMultipleChoices && code < http.StatusBadRequest: + return white + case code >= http.StatusBadRequest && code < http.StatusInternalServerError: + return yellow + default: + return red + } +} + +// MethodColor is the ANSI color for appropriately logging http method to a terminal. +func (p *LogFormatterParams) MethodColor() string { + method := p.Method + + switch method { + case "GET": + return blue + case "POST": + return cyan + case "PUT": + return yellow + case "DELETE": + return red + case "PATCH": + return green + case "HEAD": + return magenta + case "OPTIONS": + return white + default: + return reset + } +} + +// ResetColor resets all escape attributes. +func (p *LogFormatterParams) ResetColor() string { + return reset } // defaultLogFormatter is the default log format function Logger middleware uses. var defaultLogFormatter = func(param LogFormatterParams) string { var statusColor, methodColor, resetColor string if param.IsTerm { - statusColor = colorForStatus(param.StatusCode) - methodColor = colorForMethod(param.Method) - resetColor = reset + statusColor = param.StatusCodeColor() + methodColor = param.MethodColor() + resetColor = param.ResetColor() } return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s", @@ -191,6 +238,8 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc { param.StatusCode = c.Writer.Status() param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String() + param.BodySize = c.Writer.Size() + if raw != "" { path = path + "?" + raw } @@ -201,37 +250,3 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc { } } } - -func colorForStatus(code int) string { - switch { - case code >= http.StatusOK && code < http.StatusMultipleChoices: - return green - case code >= http.StatusMultipleChoices && code < http.StatusBadRequest: - return white - case code >= http.StatusBadRequest && code < http.StatusInternalServerError: - return yellow - default: - return red - } -} - -func colorForMethod(method string) string { - switch method { - case "GET": - return blue - case "POST": - return cyan - case "PUT": - return yellow - case "DELETE": - return red - case "PATCH": - return green - case "HEAD": - return magenta - case "OPTIONS": - return white - default: - return reset - } -} diff --git a/logger_test.go b/logger_test.go index 8770b5fb..c551677a 100644 --- a/logger_test.go +++ b/logger_test.go @@ -257,6 +257,13 @@ func TestDefaultLogFormatter(t *testing.T) { } func TestColorForMethod(t *testing.T) { + colorForMethod := func(method string) string { + p := LogFormatterParams{ + Method: method, + } + return p.MethodColor() + } + assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 52, 109}), colorForMethod("GET"), "get should be blue") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 54, 109}), colorForMethod("POST"), "post should be cyan") assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 51, 109}), colorForMethod("PUT"), "put should be yellow") @@ -268,12 +275,24 @@ func TestColorForMethod(t *testing.T) { } func TestColorForStatus(t *testing.T) { + colorForStatus := func(code int) string { + p := LogFormatterParams{ + StatusCode: code, + } + return p.StatusCodeColor() + } + assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), colorForStatus(http.StatusOK), "2xx should be green") assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 55, 109}), colorForStatus(http.StatusMovedPermanently), "3xx should be white") assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 51, 109}), colorForStatus(http.StatusNotFound), "4xx should be yellow") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), colorForStatus(2), "other things should be red") } +func TestResetColor(t *testing.T) { + p := LogFormatterParams{} + assert.Equal(t, string([]byte{27, 91, 48, 109}), p.ResetColor()) +} + func TestErrorLogger(t *testing.T) { router := New() router.Use(ErrorLogger()) diff --git a/mode.go b/mode.go index f787b5ca..8aa84aa8 100644 --- a/mode.go +++ b/mode.go @@ -11,8 +11,8 @@ import ( "github.com/gin-gonic/gin/binding" ) -// ENV_GIN_MODE indicates environment name for gin mode. -const ENV_GIN_MODE = "GIN_MODE" +// EnvGinMode indicates environment name for gin mode. +const EnvGinMode = "GIN_MODE" const ( // DebugMode indicates gin mode is debug. @@ -44,7 +44,7 @@ var ginMode = debugCode var modeName = DebugMode func init() { - mode := os.Getenv(ENV_GIN_MODE) + mode := os.Getenv(EnvGinMode) SetMode(mode) } diff --git a/mode_test.go b/mode_test.go index cf27acd8..3dba5150 100644 --- a/mode_test.go +++ b/mode_test.go @@ -13,13 +13,13 @@ import ( ) func init() { - os.Setenv(ENV_GIN_MODE, TestMode) + os.Setenv(EnvGinMode, TestMode) } func TestSetMode(t *testing.T) { assert.Equal(t, testCode, ginMode) assert.Equal(t, TestMode, Mode()) - os.Unsetenv(ENV_GIN_MODE) + os.Unsetenv(EnvGinMode) SetMode("") assert.Equal(t, debugCode, ginMode) diff --git a/render/reader.go b/render/reader.go index ab60e53a..312af741 100644 --- a/render/reader.go +++ b/render/reader.go @@ -36,8 +36,8 @@ func (r Reader) WriteContentType(w http.ResponseWriter) { func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) { header := w.Header() for k, v := range headers { - if val := header[k]; len(val) == 0 { - header[k] = []string{v} + if header.Get(k) == "" { + header.Set(k, v) } } } diff --git a/render/render_test.go b/render/render_test.go index 3df04a17..76e29eeb 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -470,6 +470,7 @@ func TestRenderReader(t *testing.T) { body := "#!PNG some raw data" headers := make(map[string]string) headers["Content-Disposition"] = `attachment; filename="filename.png"` + headers["x-request-id"] = "requestId" err := (Reader{ ContentLength: int64(len(body)), @@ -483,4 +484,5 @@ func TestRenderReader(t *testing.T) { assert.Equal(t, "image/png", w.Header().Get("Content-Type")) assert.Equal(t, strconv.Itoa(len(body)), w.Header().Get("Content-Length")) assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition")) + assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id")) }