summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2024-05-14 10:21:04 -0300
committerEuAndreh <eu@euandre.org>2024-05-14 10:21:04 -0300
commite10252708668b1b82ee4b8bca02c43fda3115c62 (patch)
treeba450ba549dbd836b263c7a302188460efc6603b
parentInitial empty commit (diff)
downloadglaze-e10252708668b1b82ee4b8bca02c43fda3115c62.tar.gz
glaze-e10252708668b1b82ee4b8bca02c43fda3115c62.tar.xz
Initial implementation
-rw-r--r--.gitignore2
-rw-r--r--Makefile113
-rw-r--r--deps.mk0
-rw-r--r--go.mod8
-rw-r--r--go.sum4
-rwxr-xr-xmkdeps.sh10
-rw-r--r--src/cmd/main.go7
-rw-r--r--src/lib.go187
-rwxr-xr-xtests/cli-opts.sh4
-rw-r--r--tests/lib_test.go13
10 files changed, 348 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..85a298a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/*.bin
+/tests/*.bin
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0893458
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,113 @@
+.POSIX:
+DATE = 1970-01-01
+VERSION = 0.1.0
+NAME = glaze
+NAME_UC = $(NAME)
+LANGUAGES = en
+## Installation prefix. Defaults to "/usr".
+PREFIX = /usr
+BINDIR = $(PREFIX)/bin
+LIBDIR = $(PREFIX)/lib
+INCLUDEDIR = $(PREFIX)/include
+SRCDIR = $(PREFIX)/src/$(NAME)
+SHAREDIR = $(PREFIX)/share
+LOCALEDIR = $(SHAREDIR)/locale
+MANDIR = $(SHAREDIR)/man
+EXEC = ./
+## Where to store the installation. Empty by default.
+DESTDIR =
+LDLIBS =
+
+
+
+.SUFFIXES:
+.SUFFIXES: .go .bin
+
+
+
+all:
+include deps.mk
+
+
+sources = \
+ src/lib.go \
+ src/cmd/main.go \
+
+
+derived-assets = \
+ $(NAME).bin \
+ tests/lib_test.bin \
+
+side-assets = \
+ glaze.socket \
+
+
+
+## Default target. Builds all artifacts required for testing
+## and installation.
+all: $(derived-assets)
+
+
+$(NAME).bin: src/lib.go src/cmd/main.go Makefile
+ go build -o $@ -v src/cmd/main.go
+
+tests/lib_test.bin: src/lib.go tests/lib_test.go Makefile
+ go test -c -o $@ -v $*.go
+
+
+
+check-unit: tests/lib_test.bin
+ ./tests/lib_test.bin
+
+
+integration-tests = \
+ tests/cli-opts.sh \
+
+$(integration-tests): $(NAME).bin ALWAYS
+ sh $@
+
+check-integration: $(integration-tests)
+
+
+## Run all tests. Each test suite is isolated, so that a parallel
+## build can run tests at the same time. The required artifacts
+## are created if missing.
+check: check-unit check-integration
+
+
+
+## Remove *all* derived artifacts produced during the build.
+## A dedicated test asserts that this is always true.
+clean:
+ rm -rf $(derived-assets) $(side-assets)
+
+
+## Installs into $(DESTDIR)$(PREFIX). Its dependency target
+## ensures that all installable artifacts are crafted beforehand.
+install: all
+ mkdir -p \
+ '$(DESTDIR)$(BINDIR)' \
+
+ cp $(NAME).bin '$(DESTDIR)$(BINDIR)'/$(NAME)
+ for f in $(sources); do \
+ dir='$(DESTDIR)$(SRCDIR)'/"`dirname "$${f#src/}"`"; \
+ mkdir -p "$$dir"; \
+ cp -P "$$f" "$$dir"; \
+ done
+
+## Uninstalls from $(DESTDIR)$(PREFIX). This is a perfect mirror
+## of the "install" target, and removes *all* that was installed.
+## A dedicated test asserts that this is always true.
+uninstall:
+ rm -rf \
+ '$(DESTDIR)$(BINDIR)'/$(NAME) \
+ '$(DESTDIR)$(SRCDIR)' \
+
+
+## Run it locally.
+run: all
+ rm -f glaze.socket
+ ./$(NAME).bin -P '/api/socket*:retcp.socket' -P '*:../papo/src/static/' glaze.socket
+
+
+ALWAYS:
diff --git a/deps.mk b/deps.mk
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/deps.mk
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..8787ecf
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,8 @@
+module euandre.org/glaze
+
+go 1.21.5
+
+require (
+ github.com/pkg/xattr v0.4.9 // indirect
+ golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..a69db87
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,4 @@
+github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
+github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
+golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
+golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/mkdeps.sh b/mkdeps.sh
new file mode 100755
index 0000000..a6b23d5
--- /dev/null
+++ b/mkdeps.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+set -eu
+
+export LANG=POSIX.UTF-8
+
+varlist() {
+ printf '%s = \\\n' "$1"
+ sed 's|^\(.*\)$|\t\1 \\|'
+ printf '\n'
+}
diff --git a/src/cmd/main.go b/src/cmd/main.go
new file mode 100644
index 0000000..8c2fbe3
--- /dev/null
+++ b/src/cmd/main.go
@@ -0,0 +1,7 @@
+package main
+
+import "euandre.org/glaze/src"
+
+func main() {
+ glaze.Main()
+}
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)
+ }
+}
diff --git a/tests/cli-opts.sh b/tests/cli-opts.sh
new file mode 100755
index 0000000..fcb62ca
--- /dev/null
+++ b/tests/cli-opts.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -eu
+
+exit
diff --git a/tests/lib_test.go b/tests/lib_test.go
new file mode 100644
index 0000000..31259c6
--- /dev/null
+++ b/tests/lib_test.go
@@ -0,0 +1,13 @@
+package lib_test
+
+import (
+ "testing"
+
+ "euandre.org/glaze/src"
+)
+
+func TestPlaceholder(t *testing.T) {
+ if (&glaze.PatternPath{}).String() != "FIXME" {
+ t.Fail()
+ }
+}