summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fileserver.go118
1 files changed, 108 insertions, 10 deletions
diff --git a/fileserver.go b/fileserver.go
index e1754a1..5145701 100644
--- a/fileserver.go
+++ b/fileserver.go
@@ -9,6 +9,7 @@ import (
"path"
"path/filepath"
"sort"
+ "strconv"
"strings"
"time"
@@ -21,7 +22,7 @@ func init() {
mime.AddExtensionType(".cnm", "text/cnm")
}
-func listdir(w cnp.ResponseWriter, r *cnp.Request, root string, f *os.File, stat os.FileInfo) {
+func listDir(w cnp.ResponseWriter, r *cnp.Request, root string, f *os.File, stat os.FileInfo) {
if p := r.Path(); !strings.HasSuffix(p, "/") {
w.Response().SetIntent(cnp.IntentRedirect)
w.Response().SetLocation("", p+"/")
@@ -134,7 +135,7 @@ func open(dir, fpath string) (*os.File, error) {
return os.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+fpath))))
}
-func getfile(dir, pth string, strict bool) (*os.File, os.FileInfo) {
+func getFile(dir, pth string, strict bool) (*os.File, os.FileInfo) {
var f *os.File
var err error
fnames := []string{pth}
@@ -172,7 +173,7 @@ func getfile(dir, pth string, strict bool) (*os.File, os.FileInfo) {
if !strings.HasSuffix(pth, "/") {
return nil, stat
}
- f2, st2 := getfile(dir, pth+"index.cnm", true)
+ f2, st2 := getFile(dir, pth+"index.cnm", true)
if f2 != nil {
return f2, st2
}
@@ -180,10 +181,109 @@ func getfile(dir, pth string, strict bool) (*os.File, os.FileInfo) {
return f, stat
}
+func byteSelect(w cnp.ResponseWriter, r *cnp.Request, f *os.File, stat os.FileInfo) {
+ size := stat.Size()
+ sel, selq := r.Select()
+ w.Response().SetSelect(sel, selq)
+ rej := cnp.ErrorRejected{Reason: "invalid byte selector"}
+
+ ss := strings.Split(selq, "-")
+ if len(ss) != 2 {
+ panic(rej)
+ }
+
+ ua := uint64(0)
+ var err error
+ if ss[0] != "" {
+ ua, err = strconv.ParseUint(ss[0], 10, 63)
+ if err != nil {
+ panic(rej)
+ }
+ f.Seek(int64(ua), os.SEEK_SET)
+ }
+
+ fr := io.Reader(f)
+ if ss[1] != "" {
+ var ub uint64
+ ub, err = strconv.ParseUint(ss[1], 10, 63)
+ if err != nil {
+ panic(rej)
+ }
+ fr = io.LimitReader(fr, int64(ub-ua))
+ if int64(ub) < size {
+ size = int64(ub)
+ }
+ }
+ size = size - int64(ua)
+ if size < 0 {
+ size = 0
+ }
+ w.Response().SetLength(size)
+
+ if _, err := io.Copy(w, fr); err != nil {
+ panic(err)
+ }
+}
+
+func cnmSelect(w cnp.ResponseWriter, r *cnp.Request, f *os.File, stat os.FileInfo) {
+ doc, err := cnm.ParseDocument(f)
+ if err != nil {
+ panic(err)
+ }
+
+ _, selq := r.Select()
+ sdoc, err := doc.Select(selq)
+ if err != nil {
+ panic(cnp.ErrorRejected{Reason: err.Error()})
+ }
+ if sdoc == nil {
+ return
+ }
+
+ if err = sdoc.Write(w); err != nil {
+ panic(err)
+ }
+}
+
+func serveFile(w cnp.ResponseWriter, r *cnp.Request, f *os.File, stat os.FileInfo, typ string) {
+ switch sel, _ := r.Select(); sel {
+ case "":
+ w.Response().SetLength(stat.Size())
+ if _, err := io.Copy(w, f); err != nil {
+ panic(err)
+ }
+ return
+
+ case "byte":
+ byteSelect(w, r, f, stat)
+ return
+
+ case "info":
+ resp := w.Response()
+ resp.SetLength(stat.Size())
+ var buf bytes.Buffer
+ resp.Write(&buf)
+ resp.Header = cnp.NewHeader(cnp.IntentOK, nil)
+ resp.SetLength(int64(buf.Len()))
+ if _, err := io.Copy(w, &buf); err != nil {
+ panic(err)
+ }
+ return
+
+ case "cnm":
+ if typ == "text/cnm" {
+ cnmSelect(w, r, f, stat)
+ return
+ }
+ }
+
+ panic(cnp.ErrorNotSupported{Reason: "unsupported selector"})
+}
+
func server(addr, dir string) {
fmt.Printf("listening on %q, serving dir %q\n", addr, dir)
panic(cnp.ListenAndServe(addr, cnp.HandlerFunc(func(w cnp.ResponseWriter, r *cnp.Request) {
- f, stat := getfile(dir, r.Path(), false)
+ f, stat := getFile(dir, r.Path(), false)
defer f.Close()
mtime := stat.ModTime().Truncate(time.Second)
@@ -191,7 +291,7 @@ func server(addr, dir string) {
w.Response().SetModified(mtime)
w.Response().SetTime(time.Now())
if stat.IsDir() {
- listdir(w, r, dir, f, stat)
+ listDir(w, r, dir, f, stat)
return
}
@@ -203,13 +303,11 @@ func server(addr, dir string) {
return
}
- w.Response().SetLength(stat.Size())
+ typ := guessType(f.Name())
w.Response().SetName(stat.Name())
- w.Response().SetType(guessType(f.Name()))
+ w.Response().SetType(typ)
- if _, err := io.Copy(w, f); err != nil {
- panic(err)
- }
+ serveFile(w, r, f, stat, typ)
})))
}