Skip to content

Commit

Permalink
Merge branch 'master' into fix_copy_race_condition
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelgavache committed Jul 18, 2017
2 parents 17ddb4a + 7180f2b commit 041b45b
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 51 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,10 @@ func main() {
// single file
file, _ := c.FormFile("file")
log.Println(file.Filename)


// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)

c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
Expand Down Expand Up @@ -304,6 +307,9 @@ func main() {

for _, file := range files {
log.Println(file.Filename)

// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
Expand Down Expand Up @@ -400,6 +406,8 @@ func main() {

To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).

Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](http://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags).

Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`.

When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use BindWith.
Expand Down
10 changes: 6 additions & 4 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
// Credentials doesn't match, we return 401 and abort handlers chain.
c.Header("WWW-Authenticate", realm)
c.AbortWithStatus(401)
} else {
// The user credentials was found, set user's id to key AuthUserKey in this context, the userId can be read later using
// c.MustGet(gin.AuthUserKey)
c.Set(AuthUserKey, user)
return
}

// The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
// c.MustGet(gin.AuthUserKey)
c.Set(AuthUserKey, user)
return
}
}

Expand Down
4 changes: 0 additions & 4 deletions binding/msgpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,8 @@ func (msgpackBinding) Name() string {
}

func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {

if err := codec.NewDecoder(req.Body, new(codec.MsgpackHandle)).Decode(&obj); err != nil {
//var decoder *codec.Decoder = codec.NewDecoder(req.Body, &codec.MsgpackHandle)
//if err := decoder.Decode(&obj); err != nil {
return err
}
return validate(obj)

}
22 changes: 20 additions & 2 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"net"
"net/http"
"net/url"
"os"
"strings"
"time"

Expand Down Expand Up @@ -361,8 +362,7 @@ func (c *Context) QueryArray(key string) []string {
// GetQueryArray returns a slice of strings for a given query key, plus
// a boolean value whether at least one value exists for the given key.
func (c *Context) GetQueryArray(key string) ([]string, bool) {
req := c.Request
if values, ok := req.URL.Query()[key]; ok && len(values) > 0 {
if values, ok := c.Request.URL.Query()[key]; ok && len(values) > 0 {
return values, true
}
return []string{}, false
Expand Down Expand Up @@ -435,6 +435,24 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
return c.Request.MultipartForm, err
}

// SaveUploadedFile uploads the form file to specific dst.
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()

out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()

io.Copy(out, src)
return nil
}

// Bind checks the Content-Type to select a binding engine automatically,
// Depending the "Content-Type" header different bindings are used:
// "application/json" --> JSON binding
Expand Down
42 changes: 42 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,18 @@ func TestContextFormFile(t *testing.T) {
if assert.NoError(t, err) {
assert.Equal(t, "test", f.Filename)
}

assert.NoError(t, c.SaveUploadedFile(f, "test"))
}

func TestContextMultipartForm(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
mw.WriteField("foo", "bar")
w, err := mw.CreateFormFile("file", "test")
if assert.NoError(t, err) {
w.Write([]byte("test"))
}
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf)
Expand All @@ -86,6 +92,42 @@ func TestContextMultipartForm(t *testing.T) {
if assert.NoError(t, err) {
assert.NotNil(t, f)
}

assert.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test"))
}

func TestSaveUploadedOpenFailed(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
mw.Close()

c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())

f := &multipart.FileHeader{
Filename: "file",
}
assert.Error(t, c.SaveUploadedFile(f, "test"))
}

func TestSaveUploadedCreateFailed(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
w, err := mw.CreateFormFile("file", "test")
if assert.NoError(t, err) {
w.Write([]byte("test"))
}
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f, err := c.FormFile("file")
if assert.NoError(t, err) {
assert.Equal(t, "test", f.Filename)
}

assert.Error(t, c.SaveUploadedFile(f, "/"))
}

func TestContextReset(t *testing.T) {
Expand Down
20 changes: 2 additions & 18 deletions examples/upload-file/multiple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package main

import (
"fmt"
"io"
"net/http"
"os"

"github.com/gin-gonic/gin"
)
Expand All @@ -25,24 +23,10 @@ func main() {
files := form.File["files"]

for _, file := range files {
// Source
src, err := file.Open()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("file open err: %s", err.Error()))
if err := c.SaveUploadedFile(file, file.Filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
defer src.Close()

// Destination
dst, err := os.Create(file.Filename)
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("Create file err: %s", err.Error()))
return
}
defer dst.Close()

// Copy
io.Copy(dst, src)
}

c.String(http.StatusOK, fmt.Sprintf("Uploaded successfully %d files with fields name=%s and email=%s.", len(files), name, email))
Expand Down
18 changes: 2 additions & 16 deletions examples/upload-file/single/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package main

import (
"fmt"
"io"
"net/http"
"os"

"github.com/gin-gonic/gin"
)
Expand All @@ -22,23 +20,11 @@ func main() {
c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
return
}
src, err := file.Open()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("file open err: %s", err.Error()))
return
}
defer src.Close()

// Destination
dst, err := os.Create(file.Filename)
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("Create file err: %s", err.Error()))
if err := c.SaveUploadedFile(file, file.Filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
defer dst.Close()

// Copy
io.Copy(dst, src)

c.String(http.StatusOK, fmt.Sprintf("File %s uploaded successfully with fields name=%s and email=%s.", file.Filename, name, email))
})
Expand Down
14 changes: 8 additions & 6 deletions gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,19 +156,21 @@ func (engine *Engine) LoadHTMLGlob(pattern string) {
if IsDebugging() {
debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)))
engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
} else {
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))
engine.SetHTMLTemplate(templ)
return
}
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))
engine.SetHTMLTemplate(templ)
return
}

func (engine *Engine) LoadHTMLFiles(files ...string) {
if IsDebugging() {
engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
} else {
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
engine.SetHTMLTemplate(templ)
return
}
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
engine.SetHTMLTemplate(templ)
return
}

func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
Expand Down

0 comments on commit 041b45b

Please sign in to comment.