From 6760abd6fcd3d77400de4693f5d74fe739c07191 Mon Sep 17 00:00:00 2001 From: clsr Date: Sun, 27 Aug 2017 21:40:58 +0200 Subject: Add support for the draft/cnp-select and draft/cnm-selector --- fileserver.go | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file 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) }))) } -- cgit