Skip to content

Commit 7e4680a

Browse files
committed
feat: Implement embed folder and a better organisation
1 parent 9ae1c0d commit 7e4680a

11 files changed

+198
-77
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
- go: 1.13.x
1111
- go: 1.14.x
1212
- go: 1.15.x
13+
- go: 1.16.x
1314
- go: master
1415

1516
install:

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import "github.com/gin-contrib/static"
2828
See the [example](example)
2929

3030
[embedmd]:# (example/simple/example.go go)
31+
32+
#### Serve local file
3133
```go
3234
package main
3335

@@ -52,3 +54,34 @@ func main() {
5254
r.Run(":8080")
5355
}
5456
```
57+
58+
#### Serve embed folder
59+
```go
60+
package main
61+
62+
import (
63+
"embed"
64+
"fmt"
65+
"net/http"
66+
67+
"github.com/gin-contrib/static"
68+
"github.com/gin-gonic/gin"
69+
)
70+
71+
//go:embed tmp/server
72+
var server embed.FS
73+
74+
func main() {
75+
r := gin.Default()
76+
r.Use(static.Serve("/", static.EmbedFolder(server, "tmp/server")))
77+
r.GET("/ping", func(c *gin.Context) {
78+
c.String(200, "test")
79+
})
80+
r.NoRoute(func (c *gin.Context) {
81+
fmt.Printf("%s doesn't exists, redirect on /\n", c.Request.URL.Path)
82+
c.Redirect(http.StatusMovedPermanently, "/")
83+
})
84+
// Listen and Server in 0.0.0.0:8080
85+
r.Run(":8080")
86+
}
87+
```

embed_folder.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package static
2+
3+
import (
4+
"embed"
5+
"io/fs"
6+
"log"
7+
"net/http"
8+
)
9+
10+
type embedFileSystem struct {
11+
http.FileSystem
12+
}
13+
14+
func (e embedFileSystem) Exists(prefix string, path string) bool {
15+
_, err := e.Open(path)
16+
return err == nil
17+
}
18+
19+
func EmbedFolder(fsEmbed embed.FS, targetPath string) ServeFileSystem {
20+
fsys, err := fs.Sub(fsEmbed, targetPath)
21+
if err != nil {
22+
log.Fatalf("static.EmbedFolder - Invalid targetPath value - %s", err)
23+
}
24+
return embedFileSystem{
25+
FileSystem: http.FS(fsys),
26+
}
27+
}

embed_folder_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package static
2+
3+
import (
4+
"embed"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/gin-gonic/gin"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
//go:embed test/data/server
13+
var server embed.FS
14+
15+
var embedTests = []struct {
16+
targetURL string // input
17+
httpCode int // expected http code
18+
httpBody string // expected http body
19+
name string // test name
20+
}{
21+
{"/404.html", 301, "<a href=\"/\">Moved Permanently</a>.\n\n", "Unknown file"},
22+
{"/", 200, "<h1>Hello Embed</h1>", "Root"},
23+
{"/index.html", 301, "", "Root by file name automatic redirect"},
24+
{"/static.html", 200, "<h1>Hello Gin Static</h1>", "Other file"},
25+
}
26+
27+
func TestEmbedFolder(t *testing.T) {
28+
router := gin.New()
29+
router.Use(Serve("/", EmbedFolder(server, "test/data/server")))
30+
router.NoRoute(func(c *gin.Context) {
31+
fmt.Printf("%s doesn't exists, redirect on /\n", c.Request.URL.Path)
32+
c.Redirect(301, "/")
33+
})
34+
35+
for _, tt := range embedTests {
36+
w := PerformRequest(router, "GET", tt.targetURL)
37+
assert.Equal(t, tt.httpCode, w.Code, tt.name)
38+
assert.Equal(t, tt.httpBody, w.Body.String(), tt.name)
39+
}
40+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ require (
66
github.com/stretchr/testify v1.4.0
77
)
88

9-
go 1.13
9+
go 1.16

local_file.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package static
2+
3+
import (
4+
"net/http"
5+
"os"
6+
"path"
7+
"strings"
8+
9+
"github.com/gin-gonic/gin"
10+
)
11+
12+
const INDEX = "index.html"
13+
14+
type localFileSystem struct {
15+
http.FileSystem
16+
root string
17+
indexes bool
18+
}
19+
20+
func LocalFile(root string, indexes bool) *localFileSystem {
21+
return &localFileSystem{
22+
FileSystem: gin.Dir(root, indexes),
23+
root: root,
24+
indexes: indexes,
25+
}
26+
}
27+
28+
func (l *localFileSystem) Exists(prefix string, filepath string) bool {
29+
if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) {
30+
name := path.Join(l.root, p)
31+
stats, err := os.Stat(name)
32+
if err != nil {
33+
return false
34+
}
35+
if stats.IsDir() {
36+
if !l.indexes {
37+
index := path.Join(name, INDEX)
38+
_, err := os.Stat(index)
39+
if err != nil {
40+
return false
41+
}
42+
}
43+
}
44+
return true
45+
}
46+
return false
47+
}

local_file_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package static
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/gin-gonic/gin"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestLocalFile(t *testing.T) {
14+
// SETUP file
15+
testRoot, _ := os.Getwd()
16+
f, err := ioutil.TempFile(testRoot, "")
17+
if err != nil {
18+
t.Error(err)
19+
}
20+
defer os.Remove(f.Name())
21+
f.WriteString("Gin Web Framework")
22+
f.Close()
23+
24+
dir, filename := filepath.Split(f.Name())
25+
router := gin.New()
26+
router.Use(Serve("/", LocalFile(dir, true)))
27+
28+
w := PerformRequest(router, "GET", "/"+filename)
29+
assert.Equal(t, w.Code, 200)
30+
assert.Equal(t, w.Body.String(), "Gin Web Framework")
31+
32+
w = PerformRequest(router, "GET", "/")
33+
assert.Contains(t, w.Body.String(), `<a href="`+filename)
34+
}

serve.go

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,20 @@ package static
22

33
import (
44
"net/http"
5-
"os"
6-
"path"
7-
"strings"
85

96
"github.com/gin-gonic/gin"
107
)
118

12-
const INDEX = "index.html"
13-
149
type ServeFileSystem interface {
1510
http.FileSystem
1611
Exists(prefix string, path string) bool
1712
}
1813

19-
type localFileSystem struct {
20-
http.FileSystem
21-
root string
22-
indexes bool
23-
}
24-
25-
func LocalFile(root string, indexes bool) *localFileSystem {
26-
return &localFileSystem{
27-
FileSystem: gin.Dir(root, indexes),
28-
root: root,
29-
indexes: indexes,
30-
}
31-
}
32-
33-
func (l *localFileSystem) Exists(prefix string, filepath string) bool {
34-
if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) {
35-
name := path.Join(l.root, p)
36-
stats, err := os.Stat(name)
37-
if err != nil {
38-
return false
39-
}
40-
if stats.IsDir() {
41-
if !l.indexes {
42-
index := path.Join(name, INDEX)
43-
_, err := os.Stat(index)
44-
if err != nil {
45-
return false
46-
}
47-
}
48-
}
49-
return true
50-
}
51-
return false
52-
}
53-
5414
func ServeRoot(urlPrefix, root string) gin.HandlerFunc {
5515
return Serve(urlPrefix, LocalFile(root, false))
5616
}
5717

58-
// Static returns a middleware handler that serves static files in the given directory.
18+
// Serve returns a middleware handler that serves static files in the given directory.
5919
func Serve(urlPrefix string, fs ServeFileSystem) gin.HandlerFunc {
6020
fileserver := http.FileServer(fs)
6121
if urlPrefix != "" {

serve_test.go

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/stretchr/testify/assert"
1414
)
1515

16-
func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
16+
func PerformRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
1717
req, _ := http.NewRequest(method, path, nil)
1818
w := httptest.NewRecorder()
1919
r.ServeHTTP(w, req)
@@ -44,18 +44,18 @@ func TestEmptyDirectory(t *testing.T) {
4444
router.GET("/"+filename, func(c *gin.Context) {
4545
c.String(200, "this is not printed")
4646
})
47-
w := performRequest(router, "GET", "/")
47+
w := PerformRequest(router, "GET", "/")
4848
assert.Equal(t, w.Code, 200)
4949
assert.Equal(t, w.Body.String(), "index")
5050

51-
w = performRequest(router, "GET", "/"+filename)
51+
w = PerformRequest(router, "GET", "/"+filename)
5252
assert.Equal(t, w.Code, 200)
5353
assert.Equal(t, w.Body.String(), "Gin Web Framework")
5454

55-
w = performRequest(router, "GET", "/"+filename+"a")
55+
w = PerformRequest(router, "GET", "/"+filename+"a")
5656
assert.Equal(t, w.Code, 404)
5757

58-
w = performRequest(router, "GET", "/a")
58+
w = PerformRequest(router, "GET", "/a")
5959
assert.Equal(t, w.Code, 200)
6060
assert.Equal(t, w.Body.String(), "a")
6161

@@ -65,23 +65,23 @@ func TestEmptyDirectory(t *testing.T) {
6565
c.String(200, "this is printed")
6666
})
6767

68-
w = performRequest(router2, "GET", "/")
68+
w = PerformRequest(router2, "GET", "/")
6969
assert.Equal(t, w.Code, 404)
7070

71-
w = performRequest(router2, "GET", "/static")
71+
w = PerformRequest(router2, "GET", "/static")
7272
assert.Equal(t, w.Code, 404)
7373
router2.GET("/static", func(c *gin.Context) {
7474
c.String(200, "index")
7575
})
7676

77-
w = performRequest(router2, "GET", "/static")
77+
w = PerformRequest(router2, "GET", "/static")
7878
assert.Equal(t, w.Code, 200)
7979

80-
w = performRequest(router2, "GET", "/"+filename)
80+
w = PerformRequest(router2, "GET", "/"+filename)
8181
assert.Equal(t, w.Code, 200)
8282
assert.Equal(t, w.Body.String(), "this is printed")
8383

84-
w = performRequest(router2, "GET", "/static/"+filename)
84+
w = PerformRequest(router2, "GET", "/static/"+filename)
8585
assert.Equal(t, w.Code, 200)
8686
assert.Equal(t, w.Body.String(), "Gin Web Framework")
8787
}
@@ -102,33 +102,10 @@ func TestIndex(t *testing.T) {
102102
router := gin.New()
103103
router.Use(ServeRoot("/", dir))
104104

105-
w := performRequest(router, "GET", "/"+filename)
105+
w := PerformRequest(router, "GET", "/"+filename)
106106
assert.Equal(t, w.Code, 301)
107107

108-
w = performRequest(router, "GET", "/")
108+
w = PerformRequest(router, "GET", "/")
109109
assert.Equal(t, w.Code, 200)
110110
assert.Equal(t, w.Body.String(), "index")
111111
}
112-
113-
func TestListIndex(t *testing.T) {
114-
// SETUP file
115-
testRoot, _ := os.Getwd()
116-
f, err := ioutil.TempFile(testRoot, "")
117-
if err != nil {
118-
t.Error(err)
119-
}
120-
defer os.Remove(f.Name())
121-
f.WriteString("Gin Web Framework")
122-
f.Close()
123-
124-
dir, filename := filepath.Split(f.Name())
125-
router := gin.New()
126-
router.Use(Serve("/", LocalFile(dir, true)))
127-
128-
w := performRequest(router, "GET", "/"+filename)
129-
assert.Equal(t, w.Code, 200)
130-
assert.Equal(t, w.Body.String(), "Gin Web Framework")
131-
132-
w = performRequest(router, "GET", "/")
133-
assert.Contains(t, w.Body.String(), `<a href="`+filename)
134-
}

test/data/server/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>Hello Embed</h1>

test/data/server/static.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>Hello Gin Static</h1>

0 commit comments

Comments
 (0)