mirror of
https://github.com/labstack/echo.git
synced 2025-06-23 00:38:07 +02:00
29
context.go
29
context.go
@ -7,7 +7,6 @@ import (
|
|||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@ -134,6 +133,11 @@ type (
|
|||||||
// Echo returns the `Echo` instance.
|
// Echo returns the `Echo` instance.
|
||||||
Echo() *Echo
|
Echo() *Echo
|
||||||
|
|
||||||
|
// ServeContent sends static content from `io.Reader` and handles caching
|
||||||
|
// via `If-Modified-Since` request header. It automatically sets `Content-Type`
|
||||||
|
// and `Last-Modified` response headers.
|
||||||
|
ServeContent(http.File) error
|
||||||
|
|
||||||
// Object returns the `context` instance.
|
// Object returns the `context` instance.
|
||||||
Object() *context
|
Object() *context
|
||||||
|
|
||||||
@ -376,10 +380,8 @@ func (c *context) File(file string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
}
|
}
|
||||||
fi, _ = f.Stat()
|
|
||||||
}
|
}
|
||||||
|
return c.ServeContent(f)
|
||||||
return ServeContent(c.Request(), c.Response(), f, fi)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) Attachment(r io.Reader, name string) (err error) {
|
func (c *context) Attachment(r io.Reader, name string) (err error) {
|
||||||
@ -420,13 +422,24 @@ func (c *context) Object() *context {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeContent sends a response from `io.Reader`. It automatically sets the `Content-Type`
|
func (c *context) ServeContent(f http.File) error {
|
||||||
// and `Last-Modified` headers.
|
rq := c.Request()
|
||||||
func ServeContent(rq engine.Request, rs engine.Response, f http.File, fi os.FileInfo) error {
|
rs := c.Response()
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, err := time.Parse(http.TimeFormat, rq.Header().Get(IfModifiedSince)); err == nil && fi.ModTime().Before(t.Add(1*time.Second)) {
|
||||||
|
rs.Header().Del(ContentType)
|
||||||
|
rs.Header().Del(ContentLength)
|
||||||
|
return c.NoContent(http.StatusNotModified)
|
||||||
|
}
|
||||||
|
|
||||||
rs.Header().Set(ContentType, detectContentType(fi.Name()))
|
rs.Header().Set(ContentType, detectContentType(fi.Name()))
|
||||||
rs.Header().Set(LastModified, fi.ModTime().UTC().Format(http.TimeFormat))
|
rs.Header().Set(LastModified, fi.ModTime().UTC().Format(http.TimeFormat))
|
||||||
rs.WriteHeader(http.StatusOK)
|
rs.WriteHeader(http.StatusOK)
|
||||||
_, err := io.Copy(rs, f)
|
_, err = io.Copy(rs, f)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +239,33 @@ func TestContextNetContext(t *testing.T) {
|
|||||||
// assert.Equal(t, "val", c.Value("key"))
|
// assert.Equal(t, "val", c.Value("key"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextServeContent(t *testing.T) {
|
||||||
|
e := New()
|
||||||
|
rq := test.NewRequest(GET, "/", nil)
|
||||||
|
rc := test.NewResponseRecorder()
|
||||||
|
c := NewContext(rq, rc, e)
|
||||||
|
|
||||||
|
// Not cached
|
||||||
|
fs := http.Dir("_fixture/images")
|
||||||
|
f, err := fs.Open("walle.png")
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
if assert.NoError(t, c.ServeContent(f)) {
|
||||||
|
assert.Equal(t, http.StatusOK, rc.Status())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cached
|
||||||
|
rc = test.NewResponseRecorder()
|
||||||
|
c = NewContext(rq, rc, e)
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
rq.Header().Set(IfModifiedSince, fi.ModTime().UTC().Format(http.TimeFormat))
|
||||||
|
if assert.NoError(t, c.ServeContent(f)) {
|
||||||
|
assert.Equal(t, http.StatusNotModified, rc.Status())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testBindOk(t *testing.T, c Context, ct string) {
|
func testBindOk(t *testing.T, c Context, ct string) {
|
||||||
c.Request().Header().Set(ContentType, ct)
|
c.Request().Header().Set(ContentType, ct)
|
||||||
u := new(user)
|
u := new(user)
|
||||||
|
1
echo.go
1
echo.go
@ -176,6 +176,7 @@ const (
|
|||||||
ContentEncoding = "Content-Encoding"
|
ContentEncoding = "Content-Encoding"
|
||||||
ContentLength = "Content-Length"
|
ContentLength = "Content-Length"
|
||||||
ContentType = "Content-Type"
|
ContentType = "Content-Type"
|
||||||
|
IfModifiedSince = "If-Modified-Since"
|
||||||
LastModified = "Last-Modified"
|
LastModified = "Last-Modified"
|
||||||
Location = "Location"
|
Location = "Location"
|
||||||
Upgrade = "Upgrade"
|
Upgrade = "Upgrade"
|
||||||
|
@ -61,7 +61,7 @@ func StaticFromConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
/* NOTE:
|
/* NOTE:
|
||||||
Not checking the Last-Modified header as it caches the response `304` when
|
Not checking the Last-Modified header as it caches the response `304` when
|
||||||
changing differnt directories for the same path.
|
changing different directories for the same path.
|
||||||
*/
|
*/
|
||||||
d := f
|
d := f
|
||||||
|
|
||||||
@ -97,9 +97,8 @@ func StaticFromConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
return next.Handle(c)
|
return next.Handle(c)
|
||||||
}
|
}
|
||||||
fi, _ = f.Stat() // Index file stat
|
|
||||||
}
|
}
|
||||||
return echo.ServeContent(c.Request(), c.Response(), f, fi)
|
return c.ServeContent(f)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user