cranberry.git

commit 103b8c98a460b7af5d65ad3251d048c58c4c1ad4

Author: Adam Pioterek <apioterek@consdata.com>

initial

 go.mod | 5 ++
 go.sum | 2 +
 main.go | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..0011862ced5e8d4b2fe0050fd66c5727c1de7657
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module apiote.xyz/p/cranberry
+
+go 1.22
+
+require github.com/google/uuid v1.6.0




diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..7790d7c3e03900e267f0aade3acce649895b2246
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=




diff --git a/main.go b/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..264b5de5abfc1e1abc9c645c573fce9bcb4fbe30
--- /dev/null
+++ b/main.go
@@ -0,0 +1,118 @@
+package main
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	"io/fs"
+	"log"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/uuid"
+)
+
+func handleRoot(method, reqID string, w http.ResponseWriter) {
+	if !(method == "GET" || method == "") {
+		log.Printf("%s | 405", reqID)
+		w.WriteHeader(http.StatusMethodNotAllowed)
+		return
+	}
+	w.Write([]byte("cache works"))
+	return
+
+}
+
+func getFile(storagePath, key, reqID string, w io.Writer) int {
+	file, err := os.Open(filepath.Join(storagePath, key))
+	if err != nil {
+		if errors.Is(err, fs.ErrNotExist) {
+			return http.StatusNotFound
+		}
+		log.Printf("%s | while opening file to read: %v", reqID, err)
+		return http.StatusInternalServerError
+	}
+	defer file.Close()
+	_, err = io.Copy(w, file)
+	if err != nil {
+		log.Printf("%s | while copying to response: %v", reqID, err)
+		return http.StatusInternalServerError
+	}
+	return http.StatusOK
+}
+
+func putFile(storagePath, key, reqID string, w io.Writer, body io.Reader) int {
+	file, err := os.Create(filepath.Join(storagePath, key))
+	if err != nil {
+		log.Printf("%s | while opening file to write: %v", reqID, err)
+		return http.StatusInternalServerError
+	}
+	defer file.Close()
+	_, err = io.Copy(file, body)
+	if err != nil {
+		log.Printf("%s | while copying to file: %v", reqID, err)
+		return http.StatusInternalServerError
+	}
+	return http.StatusAccepted
+}
+
+func handler(w http.ResponseWriter, r *http.Request, storagePath string) {
+	reqID := uuid.New()
+	log.Printf("%s | %s %s\n", reqID.String(), r.Method, r.URL.EscapedPath())
+
+	path := strings.Split(r.URL.Path, "/")[1:]
+	if len(path) != 1 {
+		log.Printf("%s | 404", reqID.String())
+		w.WriteHeader(http.StatusNotFound)
+		return
+	}
+
+	key := path[0]
+	if key == "" {
+		handleRoot(r.Method, reqID.String(), w)
+		return
+	}
+
+	if !filepath.IsLocal(key) {
+		log.Printf("%s | 400", reqID)
+		w.WriteHeader(http.StatusBadRequest)
+		return
+	}
+
+	switch r.Method {
+	case "GET":
+		fallthrough
+	case "":
+		status := getFile(storagePath, key, reqID.String(), w)
+		log.Printf("%s | %d\n", reqID.String(), status)
+		w.WriteHeader(status)
+		return
+	case "PUT":
+		status := putFile(storagePath, key, reqID.String(), w, r.Body)
+		log.Printf("%s | %d\n", reqID.String(), status)
+		w.WriteHeader(status)
+		return
+	default:
+		log.Printf("%s | 405", reqID)
+		w.WriteHeader(http.StatusMethodNotAllowed)
+		return
+	}
+}
+
+func main() {
+	storagePath := flag.String("s", "/var/lib/cranberry", "storage path")
+	port := flag.Int("p", 8080, "port")
+	host := flag.String("h", "", "host")
+	flag.Parse()
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		handler(w, r, *storagePath)
+	})
+	address := fmt.Sprintf("%s:%d", *host, *port)
+	err := http.ListenAndServe(address, nil)
+	if err != nil {
+		fmt.Printf("while listening: %v", err)
+	}
+}