tProvide basic GET/PUT HTTP methods - partage - File upload system
 (HTM) git clone git://git.z3bra.org/partage.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 598b884e65c18c8a239b87bb3c931c570027124a
 (HTM) Author: Willy Goiffon <dev@z3bra.org>
       Date:   Sun, 10 Oct 2021 07:29:51 +0200
       
       Provide basic GET/PUT HTTP methods
       
       Diffstat:
         A config.mk                           |       2 ++
         A go.mod                              |       3 +++
         A mkfile                              |      21 +++++++++++++++++++++
         A partage.go                          |     157 +++++++++++++++++++++++++++++++
       
       4 files changed, 183 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/config.mk b/config.mk
       t@@ -0,0 +1,2 @@
       +GO   = go
       +GOOS = `{uname -s | tr A-Z a-z}
 (DIR) diff --git a/go.mod b/go.mod
       t@@ -0,0 +1,3 @@
       +module git.z3bra.org/partage
       +
       +go 1.17
 (DIR) diff --git a/mkfile b/mkfile
       t@@ -0,0 +1,21 @@
       +<config.mk
       +
       +all:V: partage
       +
       +%: %.go
       +        $GO build -o $stem
       +
       +clean:V:
       +        rm -f partage
       +
       +install:V: partage
       +        mkdir -p ${DESTDIR}${PREFIX}/bin
       +        cp partage ${DESTDIR}${PREFIX}/bin/partage
       +        chmod 755 ${DESTDIR}${PREFIX}/bin/partage
       +        mkdir -p ${DESTDIR}${MANDIR}/man1
       +        cp partage.1 ${DESTDIR}${MANDIR}/man1/partage.1
       +        chmod 644 ${DESTDIR}${MANDIR}/man1/partage.1
       +
       +uninstall:V:
       +        rm ${DESTDIR}${PREFIX}/bin/partage
       +        rm ${DESTDIR}${MANDIR}/man1/partage.1
 (DIR) diff --git a/partage.go b/partage.go
       t@@ -0,0 +1,157 @@
       +package main
       +
       +import (
       +        "fmt"
       +        "io"
       +        "io/ioutil"
       +        "net/http"
       +        "os"
       +        "path"
       +        "path/filepath"
       +)
       +
       +var conf struct {
       +        bind     string
       +        filepath string
       +        rootdir  string
       +        baseuri  string
       +        maxsize  int64
       +}
       +
       +func contenttype(f *os.File) string {
       +        buffer := make([]byte, 512)
       +
       +        _, err := f.Read(buffer)
       +        if err != nil {
       +                return ""
       +        }
       +
       +        mime := http.DetectContentType(buffer)
       +
       +        return mime
       +}
       +
       +func writefile(f *os.File, s io.ReadCloser) int {
       +        buffer := make([]byte, 4096)
       +
       +        var sz int
       +
       +        for {
       +                n, err := s.Read(buffer)
       +
       +                if err != nil {
       +                        if err == io.EOF {
       +                                n, err := f.Write(buffer[:n])
       +                                if err != nil {
       +                                        fmt.Println(err)
       +                                }
       +                                sz += n
       +                                break
       +                        }
       +                        fmt.Println(err)
       +                        return -1
       +                }
       +
       +                n, err = f.Write(buffer[:n])
       +                if err != nil {
       +                        fmt.Println(err)
       +                }
       +                sz += n
       +        }
       +
       +        f.Sync()
       +        return sz
       +}
       +
       +func servefile(f *os.File, w http.ResponseWriter) {
       +        buffer := make([]byte, 4096)
       +
       +        mime := contenttype(f)
       +        w.Header().Set("Content-Type", mime)
       +
       +        f.Seek(0, 0)
       +        for {
       +                n, err := f.Read(buffer)
       +
       +                if err != nil {
       +                        if err == io.EOF {
       +                                _, err := w.Write(buffer[:n])
       +                                if err != nil {
       +                                        fmt.Println(err)
       +                                }
       +                                break
       +                        }
       +                        fmt.Println(err)
       +                        return
       +                }
       +
       +                _, err = w.Write(buffer[:n])
       +                if err != nil {
       +                        fmt.Println(err)
       +                }
       +        }
       +}
       +
       +func parse(w http.ResponseWriter, r *http.Request) {
       +
       +        fmt.Printf("%s %s\n", r.Method, r.URL.Path)
       +
       +        // Max 15 Gb uploads
       +        if r.ContentLength > conf.maxsize {
       +                w.WriteHeader(http.StatusRequestEntityTooLarge)
       +                w.Write([]byte("File is too big"))
       +        }
       +
       +        err := r.ParseForm()
       +        if err != nil {
       +                fmt.Printf("%s %s: %s", r.Method, r.URL.Path, err)
       +        }
       +
       +        switch r.Method {
       +        case "PUT":
       +                tmp, _ := ioutil.TempFile(conf.filepath, "*"+path.Ext(r.URL.Path))
       +                f, err := os.Create(tmp.Name())
       +                if err != nil {
       +                        fmt.Println(err)
       +                        return
       +                }
       +                defer f.Close()
       +
       +                if writefile(f, r.Body) < 0 {
       +                        w.WriteHeader(http.StatusInternalServerError)
       +                        return
       +                }
       +
       +                resp := conf.baseuri + "/" + filepath.Base(tmp.Name())
       +                w.Write([]byte(resp))
       +
       +        case "GET":
       +                // r.URL.Path is sanitized regarding "." and ".."
       +                filename := r.URL.Path
       +                if r.URL.Path == "/" {
       +                        filename = "/index.html"
       +                }
       +                
       +                f, err := os.Open(conf.rootdir + filename)
       +                if err != nil {
       +                        w.WriteHeader(http.StatusNotFound)
       +                        fmt.Println(err)
       +                        return
       +                }
       +                defer f.Close()
       +
       +                servefile(f, w)
       +        }
       +}
       +
       +func main() {
       +
       +        conf.bind = "0.0.0.0:8080"
       +        conf.maxsize = 28 * 1024 * 1024
       +        conf.filepath = "/tmp"
       +        conf.rootdir = "./static"
       +        conf.baseuri = "http://192.168.0.3:8080"
       +
       +        http.HandleFunc("/", parse)
       +        http.ListenAndServe("0.0.0.0:8080", nil)
       +}