mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-24 02:32:17 +08:00
Merge branch 'master' into example3332
This commit is contained in:
commit
75f42a40b8
4
auth.go
4
auth.go
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
@ -444,14 +445,15 @@ func TestContextPostFormMultipart(t *testing.T) {
|
|||||||
c.Request = createMultipartRequest()
|
c.Request = createMultipartRequest()
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
Foo string `form:"foo"`
|
Foo string `form:"foo"`
|
||||||
Bar string `form:"bar"`
|
Bar string `form:"bar"`
|
||||||
BarAsInt int `form:"bar"`
|
BarAsInt int `form:"bar"`
|
||||||
Array []string `form:"array"`
|
Array []string `form:"array"`
|
||||||
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"`
|
||||||
BlankTime time.Time `form:"blank_time" time_format:"02/01/2006 15:04"`
|
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"`
|
||||||
}
|
}
|
||||||
assert.NoError(t, c.Bind(&obj))
|
assert.NoError(t, c.Bind(&obj))
|
||||||
assert.Equal(t, obj.Foo, "bar")
|
assert.Equal(t, obj.Foo, "bar")
|
||||||
@ -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")
|
||||||
|
6
debug.go
6
debug.go
@ -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
|
||||||
|
@ -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
51
gin.go
@ -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}
|
||||||
|
2
mode.go
2
mode.go
@ -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)
|
||||||
|
@ -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)
|
||||||
|
11
utils.go
11
utils.go
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user