Merge branch 'master' into example3332

This commit is contained in:
Bo-Yi Wu 2017-09-29 16:42:54 +08:00 committed by GitHub
commit 75f42a40b8
9 changed files with 84 additions and 41 deletions

View File

@ -24,7 +24,7 @@ type authPair struct {
type authPairs []authPair type authPairs []authPair
func (a authPairs) searchCredential(authValue string) (string, bool) { func (a authPairs) searchCredential(authValue string) (string, bool) {
if len(authValue) == 0 { if authValue == "" {
return "", false return "", false
} }
for _, pair := range a { for _, pair := range a {
@ -71,7 +71,7 @@ func processAccounts(accounts Accounts) authPairs {
assert1(len(accounts) > 0, "Empty list of authorized credentials") assert1(len(accounts) > 0, "Empty list of authorized credentials")
pairs := make(authPairs, 0, len(accounts)) pairs := make(authPairs, 0, len(accounts))
for user, password := range accounts { for user, password := range accounts {
assert1(len(user) > 0, "User can not be empty") assert1(user != "", "User can not be empty")
value := authorizationHeader(user, password) value := authorizationHeader(user, password)
pairs = append(pairs, authPair{ pairs = append(pairs, authPair{
Value: value, Value: value,

View File

@ -163,6 +163,14 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
l = time.UTC l = time.UTC
} }
if locTag := structField.Tag.Get("time_location"); locTag != "" {
loc, err := time.LoadLocation(locTag)
if err != nil {
return err
}
l = loc
}
t, err := time.ParseInLocation(timeFormat, val, l) t, err := time.ParseInLocation(timeFormat, val, l)
if err != nil { if err != nil {
return err return err

View File

@ -45,6 +45,7 @@ func createMultipartRequest() *http.Request {
must(mw.WriteField("id", "")) must(mw.WriteField("id", ""))
must(mw.WriteField("time_local", "31/12/2016 14:55")) must(mw.WriteField("time_local", "31/12/2016 14:55"))
must(mw.WriteField("time_utc", "31/12/2016 14:55")) must(mw.WriteField("time_utc", "31/12/2016 14:55"))
must(mw.WriteField("time_location", "31/12/2016 14:55"))
req, err := http.NewRequest("POST", "/", body) req, err := http.NewRequest("POST", "/", body)
must(err) must(err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
@ -451,6 +452,7 @@ func TestContextPostFormMultipart(t *testing.T) {
ID string `form:"id"` ID string `form:"id"`
TimeLocal time.Time `form:"time_local" time_format:"02/01/2006 15:04"` TimeLocal time.Time `form:"time_local" time_format:"02/01/2006 15:04"`
TimeUTC time.Time `form:"time_utc" time_format:"02/01/2006 15:04" time_utc:"1"` TimeUTC time.Time `form:"time_utc" time_format:"02/01/2006 15:04" time_utc:"1"`
TimeLocation time.Time `form:"time_location" time_format:"02/01/2006 15:04" time_location:"Asia/Tokyo"`
BlankTime time.Time `form:"blank_time" time_format:"02/01/2006 15:04"` BlankTime time.Time `form:"blank_time" time_format:"02/01/2006 15:04"`
} }
assert.NoError(t, c.Bind(&obj)) assert.NoError(t, c.Bind(&obj))
@ -463,6 +465,9 @@ func TestContextPostFormMultipart(t *testing.T) {
assert.Equal(t, obj.TimeLocal.Location(), time.Local) assert.Equal(t, obj.TimeLocal.Location(), time.Local)
assert.Equal(t, obj.TimeUTC.Format("02/01/2006 15:04"), "31/12/2016 14:55") assert.Equal(t, obj.TimeUTC.Format("02/01/2006 15:04"), "31/12/2016 14:55")
assert.Equal(t, obj.TimeUTC.Location(), time.UTC) assert.Equal(t, obj.TimeUTC.Location(), time.UTC)
loc, _ := time.LoadLocation("Asia/Tokyo")
assert.Equal(t, obj.TimeLocation.Format("02/01/2006 15:04"), "31/12/2016 14:55")
assert.Equal(t, obj.TimeLocation.Location(), loc)
assert.True(t, obj.BlankTime.IsZero()) assert.True(t, obj.BlankTime.IsZero())
value, ok := c.GetQuery("foo") value, ok := c.GetQuery("foo")

View File

@ -46,6 +46,12 @@ func debugPrint(format string, values ...interface{}) {
} }
} }
func debugPrintWARNINGDefault() {
debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
`)
}
func debugPrintWARNINGNew() { func debugPrintWARNINGNew() {
debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production. debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release - using env: export GIN_MODE=release

View File

@ -86,6 +86,24 @@ func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) {
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", w.String()) assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", w.String())
} }
func TestDebugPrintWARNINGDefault(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
debugPrintWARNINGDefault()
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", w.String())
}
func TestDebugPrintWARNINGNew(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
debugPrintWARNINGNew()
assert.Equal(t, "[GIN-debug] [WARNING] Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:\texport GIN_MODE=release\n - using code:\tgin.SetMode(gin.ReleaseMode)\n\n", w.String())
}
func setup(w io.Writer) { func setup(w io.Writer) {
SetMode(DebugMode) SetMode(DebugMode)
log.SetOutput(w) log.SetOutput(w)

51
gin.go
View File

@ -20,9 +20,11 @@ const (
defaultMultipartMemory = 32 << 20 // 32 MB defaultMultipartMemory = 32 << 20 // 32 MB
) )
var default404Body = []byte("404 page not found") var (
var default405Body = []byte("405 method not allowed") default404Body = []byte("404 page not found")
var defaultAppEngine bool default405Body = []byte("405 method not allowed")
defaultAppEngine bool
)
type HandlerFunc func(*Context) type HandlerFunc func(*Context)
type HandlersChain []HandlerFunc type HandlersChain []HandlerFunc
@ -91,6 +93,7 @@ type Engine struct {
// If enabled, the url.RawPath will be used to find parameters. // If enabled, the url.RawPath will be used to find parameters.
UseRawPath bool UseRawPath bool
// If true, the path value will be unescaped. // If true, the path value will be unescaped.
// If UseRawPath is false (by default), the UnescapePathValues effectively is true, // If UseRawPath is false (by default), the UnescapePathValues effectively is true,
// as url.Path gonna be used, which is already unescaped. // as url.Path gonna be used, which is already unescaped.
@ -141,6 +144,7 @@ func New() *Engine {
// Default returns an Engine instance with the Logger and Recovery middleware already attached. // Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine { func Default() *Engine {
debugPrintWARNINGDefault()
engine := New() engine := New()
engine.Use(Logger(), Recovery()) engine.Use(Logger(), Recovery())
return engine return engine
@ -161,13 +165,16 @@ func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
} }
func (engine *Engine) LoadHTMLGlob(pattern string) { func (engine *Engine) LoadHTMLGlob(pattern string) {
left := engine.delims.Left
right := engine.delims.Right
if IsDebugging() { if IsDebugging() {
debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))) debugPrintLoadTemplate(template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern)))
engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims} engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
return return
} }
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)) templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
engine.SetHTMLTemplate(templ) engine.SetHTMLTemplate(templ)
} }
@ -225,7 +232,7 @@ func (engine *Engine) rebuild405Handlers() {
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
assert1(path[0] == '/', "path must begin with '/'") assert1(path[0] == '/', "path must begin with '/'")
assert1(len(method) > 0, "HTTP method can not be empty") assert1(method != "", "HTTP method can not be empty")
assert1(len(handlers) > 0, "there must be at least one handler") assert1(len(handlers) > 0, "there must be at least one handler")
debugPrintRoute(method, path, handlers) debugPrintRoute(method, path, handlers)
@ -322,12 +329,12 @@ func (engine *Engine) HandleContext(c *Context) {
engine.pool.Put(c) engine.pool.Put(c)
} }
func (engine *Engine) handleHTTPRequest(context *Context) { func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := context.Request.Method httpMethod := c.Request.Method
path := context.Request.URL.Path path := c.Request.URL.Path
unescape := false unescape := false
if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 { if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
path = context.Request.URL.RawPath path = c.Request.URL.RawPath
unescape = engine.UnescapePathValues unescape = engine.UnescapePathValues
} }
@ -337,20 +344,20 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
if t[i].method == httpMethod { if t[i].method == httpMethod {
root := t[i].root root := t[i].root
// Find route in tree // Find route in tree
handlers, params, tsr := root.getValue(path, context.Params, unescape) handlers, params, tsr := root.getValue(path, c.Params, unescape)
if handlers != nil { if handlers != nil {
context.handlers = handlers c.handlers = handlers
context.Params = params c.Params = params
context.Next() c.Next()
context.writermem.WriteHeaderNow() c.writermem.WriteHeaderNow()
return return
} }
if httpMethod != "CONNECT" && path != "/" { if httpMethod != "CONNECT" && path != "/" {
if tsr && engine.RedirectTrailingSlash { if tsr && engine.RedirectTrailingSlash {
redirectTrailingSlash(context) redirectTrailingSlash(c)
return return
} }
if engine.RedirectFixedPath && redirectFixedPath(context, root, engine.RedirectFixedPath) { if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
return return
} }
} }
@ -362,15 +369,15 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
for _, tree := range engine.trees { for _, tree := range engine.trees {
if tree.method != httpMethod { if tree.method != httpMethod {
if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil { if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
context.handlers = engine.allNoMethod c.handlers = engine.allNoMethod
serveError(context, 405, default405Body) serveError(c, 405, default405Body)
return return
} }
} }
} }
} }
context.handlers = engine.allNoRoute c.handlers = engine.allNoRoute
serveError(context, 404, default404Body) serveError(c, 404, default404Body)
} }
var mimePlain = []string{MIMEPlain} var mimePlain = []string{MIMEPlain}

View File

@ -39,7 +39,7 @@ var modeName = DebugMode
func init() { func init() {
mode := os.Getenv(ENV_GIN_MODE) mode := os.Getenv(ENV_GIN_MODE)
if len(mode) == 0 { if mode == "" {
SetMode(DebugMode) SetMode(DebugMode)
} else { } else {
SetMode(mode) SetMode(mode)

View File

@ -60,7 +60,7 @@ func (r HTMLDebug) loadTemplate() *template.Template {
if len(r.Files) > 0 { if len(r.Files) > 0 {
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...)) return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...))
} }
if len(r.Glob) > 0 { if r.Glob != "" {
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob)) return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob))
} }
panic("the HTML debug render was created without files or glob pattern") panic("the HTML debug render was created without files or glob pattern")
@ -69,7 +69,7 @@ func (r HTMLDebug) loadTemplate() *template.Template {
func (r HTML) Render(w http.ResponseWriter) error { func (r HTML) Render(w http.ResponseWriter) error {
r.WriteContentType(w) r.WriteContentType(w)
if len(r.Name) == 0 { if r.Name == "" {
return r.Template.Execute(w, r.Data) return r.Template.Execute(w, r.Data)
} }
return r.Template.ExecuteTemplate(w, r.Name, r.Data) return r.Template.ExecuteTemplate(w, r.Name, r.Data)

View File

@ -103,7 +103,7 @@ func parseAccept(acceptHeader string) []string {
if index := strings.IndexByte(part, ';'); index >= 0 { if index := strings.IndexByte(part, ';'); index >= 0 {
part = part[0:index] part = part[0:index]
} }
if part = strings.TrimSpace(part); len(part) > 0 { if part = strings.TrimSpace(part); part != "" {
out = append(out, part) out = append(out, part)
} }
} }
@ -111,11 +111,10 @@ func parseAccept(acceptHeader string) []string {
} }
func lastChar(str string) uint8 { func lastChar(str string) uint8 {
size := len(str) if str == "" {
if size == 0 {
panic("The length of the string can't be 0") panic("The length of the string can't be 0")
} }
return str[size-1] return str[len(str)-1]
} }
func nameOfFunction(f interface{}) string { func nameOfFunction(f interface{}) string {
@ -123,7 +122,7 @@ func nameOfFunction(f interface{}) string {
} }
func joinPaths(absolutePath, relativePath string) string { func joinPaths(absolutePath, relativePath string) string {
if len(relativePath) == 0 { if relativePath == "" {
return absolutePath return absolutePath
} }
@ -138,7 +137,7 @@ func joinPaths(absolutePath, relativePath string) string {
func resolveAddress(addr []string) string { func resolveAddress(addr []string) string {
switch len(addr) { switch len(addr) {
case 0: case 0:
if port := os.Getenv("PORT"); len(port) > 0 { if port := os.Getenv("PORT"); port != "" {
debugPrint("Environment variable PORT=\"%s\"", port) debugPrint("Environment variable PORT=\"%s\"", port)
return ":" + port return ":" + port
} }