summaryrefslogtreecommitdiff
path: root/src/lib.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.go')
-rw-r--r--src/lib.go187
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)
+ }
+}