Gisle Aune
8 years ago
4 changed files with 141 additions and 2 deletions
-
4resource.go
-
8router.go
-
65static.go
-
66static_test.go
@ -0,0 +1,65 @@ |
|||||
|
package wrouter |
||||
|
|
||||
|
import ( |
||||
|
"io" |
||||
|
"mime" |
||||
|
"net/http" |
||||
|
"os" |
||||
|
"path" |
||||
|
"strings" |
||||
|
|
||||
|
"git.aiterp.net/gisle/wrouter/response" |
||||
|
|
||||
|
"git.aiterp.net/gisle/wrouter/auth" |
||||
|
) |
||||
|
|
||||
|
type Static struct { |
||||
|
path string |
||||
|
} |
||||
|
|
||||
|
func NewStatic(path string) *Static { |
||||
|
return &Static{path} |
||||
|
} |
||||
|
|
||||
|
func (static *Static) Handle(urlPath string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { |
||||
|
// Get the subpath out of the path
|
||||
|
subpath := req.URL.Path[len(urlPath):] |
||||
|
if subpath[0] == '/' { |
||||
|
subpath = subpath[1:] |
||||
|
} |
||||
|
|
||||
|
// Disallow breaking out of the folder
|
||||
|
if strings.Contains(subpath, "..") { |
||||
|
response.Text(w, 403, "No .. in paths allowed") |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
// Try loading the file
|
||||
|
filepath := path.Join(static.path, subpath) |
||||
|
info, err := os.Stat(filepath) |
||||
|
if err != nil || info.IsDir() { |
||||
|
return false |
||||
|
} |
||||
|
file, err := os.Open(filepath) |
||||
|
if err != nil || file == nil { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
// Find and convert extension
|
||||
|
ep := strings.LastIndex(filepath, ".") |
||||
|
ext := "" |
||||
|
if ep != -1 { |
||||
|
ext = filepath[ep:] |
||||
|
} |
||||
|
mimeType := mime.TypeByExtension(ext) |
||||
|
if mimeType == "" { |
||||
|
mimeType = "text/plain" |
||||
|
} |
||||
|
w.Header().Set("Content-Type", mimeType) |
||||
|
|
||||
|
// Submit
|
||||
|
w.WriteHeader(200) |
||||
|
io.Copy(w, file) |
||||
|
|
||||
|
return true |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
package wrouter |
||||
|
|
||||
|
import ( |
||||
|
"net/http" |
||||
|
"net/http/httptest" |
||||
|
"strings" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
func TestStatic(t *testing.T) { |
||||
|
router := &Router{} |
||||
|
router.Static("/data", "./") |
||||
|
server := httptest.NewServer(router) |
||||
|
|
||||
|
t.Run("Download", func(t *testing.T) { |
||||
|
resp, err := http.Get(server.URL + "/data/README.md") |
||||
|
|
||||
|
if err != nil { |
||||
|
t.Error("Request:", err) |
||||
|
t.Fail() |
||||
|
} |
||||
|
|
||||
|
if resp.StatusCode != 200 { |
||||
|
t.Error("Expected 200, got", resp.Status) |
||||
|
t.Fail() |
||||
|
} |
||||
|
|
||||
|
if resp.ContentLength == 0 { |
||||
|
t.Error("No content returned from server") |
||||
|
t.Fail() |
||||
|
} |
||||
|
|
||||
|
if !strings.Contains(resp.Header.Get("Content-Type"), "text/plain") { |
||||
|
t.Errorf("Content-Type %s != %s", resp.Header.Get("Content-Type"), "text/plain") |
||||
|
t.Fail() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
t.Run("Download_Fail", func(t *testing.T) { |
||||
|
resp, err := http.Get(server.URL + "/data/f42klfk2kf2kfk.md") |
||||
|
|
||||
|
if err != nil { |
||||
|
t.Error("Request:", err) |
||||
|
t.Fail() |
||||
|
} |
||||
|
|
||||
|
if resp.StatusCode != 404 { |
||||
|
t.Error("Expected 404, got", resp.Status) |
||||
|
t.Fail() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
t.Run("Download_Fail2", func(t *testing.T) { |
||||
|
resp, err := http.Get(server.URL + "/data/../../../../../../../etc/passwd") |
||||
|
|
||||
|
if err != nil { |
||||
|
t.Error("Request:", err) |
||||
|
t.Fail() |
||||
|
} |
||||
|
|
||||
|
if resp.StatusCode != 403 { |
||||
|
t.Error("Expected 403, got", resp.Status) |
||||
|
t.Fail() |
||||
|
} |
||||
|
}) |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue