diff --git a/http/fs/fs.go b/http/fs/fs.go new file mode 100644 index 0000000..7a04e72 --- /dev/null +++ b/http/fs/fs.go @@ -0,0 +1,121 @@ +package fs + +import ( + "io/fs" + "net/http" + "os" + "path" + "strings" + "time" +) + +// ----------------------------------------------------------------------------------------- + +type unionFS struct { + fs []http.FileSystem +} + +func (p *unionFS) Open(name string) (f http.File, err error) { + for _, fs := range p.fs { + f, err = fs.Open(name) + if !os.IsNotExist(err) { + return + } + } + return nil, os.ErrNotExist +} + +func Union(fs ...http.FileSystem) http.FileSystem { + return &unionFS{fs} +} + +// ----------------------------------------------------------------------------------------- + +type filesFS struct { + files []string +} + +func (p *filesFS) Open(name string) (f http.File, err error) { + files := p.files + name = name[1:] + for i := 0; i < len(files); i += 2 { + if files[i] == name { + f, err = os.Open(files[i+1]) + return + } + } + return nil, os.ErrNotExist +} + +func Files(files ...string) http.FileSystem { + return &filesFS{files} +} + +// ----------------------------------------------------------------------------------------- + +type filesDataFS struct { + files []string +} + +type dataFileInfo struct { + r *strings.Reader + name string +} + +func (p *dataFileInfo) Name() string { + return path.Base(p.name) +} + +func (p *dataFileInfo) Size() int64 { + return p.r.Size() +} + +func (p *dataFileInfo) Mode() fs.FileMode { + return 0 +} + +func (p *dataFileInfo) ModTime() time.Time { + return time.Now() +} + +func (p *dataFileInfo) IsDir() bool { + return false +} + +func (p *dataFileInfo) Sys() interface{} { + return nil +} + +type dataFile struct { + *strings.Reader + name string +} + +func (p *dataFile) Close() error { + return nil +} + +func (p *dataFile) Readdir(count int) ([]fs.FileInfo, error) { + return nil, nil +} + +func (p *dataFile) Stat() (fs.FileInfo, error) { + return &dataFileInfo{p.Reader, p.name}, nil +} + +func (p *filesDataFS) Open(name string) (f http.File, err error) { + files := p.files + name = name[1:] + for i := 0; i < len(files); i += 2 { + if files[i] == name { + return &dataFile{strings.NewReader(files[i+1]), name}, nil + } + } + return nil, os.ErrNotExist +} + +func FilesWithContent(files ...string) http.FileSystem { + return &filesDataFS{files} +} + +// ----------------------------------------------------------------------------------------- diff --git a/http/nocache/nocache.go b/http/nocache/nocache.go new file mode 100644 index 0000000..c780e28 --- /dev/null +++ b/http/nocache/nocache.go @@ -0,0 +1,21 @@ +package nocache + +import ( + "net/http" +) + +type respWriter struct { + http.ResponseWriter +} + +func (p *respWriter) WriteHeader(statusCode int) { + w := p.ResponseWriter + w.Header().Del("Last-Modified") + w.WriteHeader(statusCode) +} + +func New(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h.ServeHTTP(&respWriter{w}, r) + }) +} diff --git a/http/tracer/tracer.go b/http/tracer/tracer.go new file mode 100644 index 0000000..3586502 --- /dev/null +++ b/http/tracer/tracer.go @@ -0,0 +1,13 @@ +package tracer + +import ( + "log" + "net/http" +) + +func New(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Printf("%s %v\n", r.Method, r.URL) + h.ServeHTTP(w, r) + }) +} diff --git a/shell/open.go b/shell/open.go new file mode 100644 index 0000000..43e16e5 --- /dev/null +++ b/shell/open.go @@ -0,0 +1,25 @@ +package shell + +import ( + "os/exec" + "runtime" +) + +// Open opens the specified URL in the default browser of the user. +// See https://stackoverflow.com/questions/39320371/how-start-web-server-to-open-page-in-browser-in-golang +func Open(url string) error { + var cmd string + var args []string + + switch runtime.GOOS { + case "windows": + cmd = "cmd" + args = []string{"/c", "start"} + case "darwin": + cmd = "open" + default: // "linux", "freebsd", "openbsd", "netbsd" + cmd = "xdg-open" + } + args = append(args, url) + return exec.Command(cmd, args...).Start() +}