diff options
Diffstat (limited to 'src/lib.go')
-rw-r--r-- | src/lib.go | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/src/lib.go b/src/lib.go new file mode 100644 index 0000000..6b486f5 --- /dev/null +++ b/src/lib.go @@ -0,0 +1,187 @@ +package glaze + +import ( + "context" + "errors" + "flag" + "io" + "io/fs" + "log" + "net" + "net/http" + "net/url" + "os" + "strings" + + "github.com/pkg/xattr" +) + + + +const TRY_INDEX_HTML_XATTR = "user.glaze.directory-listing" + + +type PatternPath struct {} + +func (i *PatternPath) String() string { + return "FIXME" +} + +func proxyHandler(path string) http.Handler { + target, err := url.Parse(path) + if err != nil { + log.Fatal(err) + } + + target.Scheme = "http" + target.Host = "localhost" + + httpClient := http.Client { + Transport: &http.Transport { + DialContext: func(_ context.Context, _ string, _ string) (net.Conn, error) { + return net.Dial("unix", path) + }, + }, + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.URL.Scheme = target.Scheme + r.URL.Host = target.Host + r.RequestURI = "" + + response, err := httpClient.Do(r) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.Println(err) + return + } + + for k, vArr := range response.Header { + for _, v := range vArr { + w.Header().Add(k, v) + } + } + w.WriteHeader(response.StatusCode) + io.Copy(w, response.Body) + }) +} + +func fileHandler(path string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fileHandle, err := os.Open(path) + if err != nil { + http.Error(w, "Not Founde", http.StatusNotFound) + return + } + + fileInfo, err := fileHandle.Stat() + if err != nil { + http.Error(w, "Server Errore", http.StatusInternalServerError) + return + } + + http.ServeContent(w, r, fileInfo.Name(), fileInfo.ModTime(), fileHandle) + }) +} + +func directoryHandler(path string) http.Handler { + // FIXME: return 301/302 on symlink + return http.FileServer(http.Dir(path)) +} + +func adjustPattern(pattern string) string { + if pattern == "*" { + return "/" + } else if strings.HasSuffix(pattern, "*") { + return pattern[0:len(pattern) - 1] + } else { + return pattern + // FIXME: when on Go 1.22, use: + // return pattern + "{$}" + } +} + +func registerHandler(tag string, pattern string, handler http.Handler) { + http.Handle( + pattern, + logged( + tag, + http.StripPrefix( + pattern, + handler, + ), + ), + ) +} + +func registerPattern(fileInfo fs.FileInfo, pattern string, path string) { + if fileInfo.Mode().Type() == fs.ModeSocket { + registerHandler("proxy", pattern, proxyHandler(path)) + } else if !fileInfo.Mode().IsDir() { + registerHandler("file", pattern, fileHandler(path)) + } else { + registerHandler("directory", pattern, directoryHandler(path)) + + data, err := xattr.Get(path, TRY_INDEX_HTML_XATTR) + if err != nil { + log.Fatal(err) + } + if string(data) == "true" { + indexPattern := pattern + if !strings.HasSuffix(indexPattern, "/") { + indexPattern += "/" + } + indexPattern += "index.html" + registerHandler("index-html-file", indexPattern, fileHandler(path + "/index.html")) + } + } +} + +func (_ *PatternPath) Set(value string) error { + arr := strings.Split(value, ":") + if len(arr) != 2 { + return errors.New("Bad value for path pattern: " + value) + } + + pattern := adjustPattern(arr[0]) + path := arr[1] + + fileInfo, err := os.Stat(path) + if err != nil { + return err + } + + registerPattern(fileInfo, pattern, path) + return nil +} + +func logged(tag string, next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Printf("%s: %s %s\n", tag, r.Method, r.URL.Path) + next.ServeHTTP(w, r) + }) +} + +// FIXME: log to json +func Main() { + var myFlags PatternPath + flag.Var(&myFlags, "P", "URL:PATH pattern") + flag.Parse() + + if flag.NArg() != 1 { + flag.Usage() + os.Exit(2) + } + listenPath := flag.Arg(0) + + listener, err := net.Listen("unix", listenPath) + if err != nil { + log.Fatal("Failed to net.listen(): " + err.Error()) + } + + server := http.Server {} + err = server.Serve(listener) + if err != nil { + log.Fatal(err) + } +} |