junk/main.go

162 lines
3.4 KiB
Go

package main
import (
"bufio"
"net/http"
"net/url"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
"time"
)
const junkaddr = "localhost:18012"
const proxybufsize = 4 << 10
type JunkConf struct {
Proxy string
Cmd *exec.Cmd
}
func main() {
status := cmain(len(os.Args), os.Args)
os.Setenv("status", status)
if status != "" {
os.Exit(1)
}
}
func proxy(w http.ResponseWriter, r *http.Request, proxy_path string) {
var err error
r.RequestURI = ""
r.URL, err = url.Parse(proxy_path)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
resp, err := http.DefaultClient.Do(r)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
for k, v := range resp.Header {
for _, x := range v {
w.Header().Add(k, x)
}
}
w.WriteHeader(resp.StatusCode)
buf := make([]byte, proxybufsize)
for {
n, err := resp.Body.Read(buf)
w.Write(buf[:n])
if err != nil {
break
}
}
}
func cmain(argc int, argv []string) string {
fileserver := http.FileServer(http.Dir(""))
cmdmap := map[string]JunkConf{}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
p1 := r.URL.Path
p := strings.Split(p1, "/")[1:]
seg := p[0]
if seg == "" {
fileserver.ServeHTTP(w, r)
return
}
vc, err := r.Cookie("view")
if err == nil && seg != vc.Value {
w.Header().Add("Location", "/"+vc.Value+p1)
w.WriteHeader(http.StatusFound)
return
}
if conf, ok := cmdmap[seg]; ok {
proxy_path := conf.Proxy + "/" + strings.Join(p[1:], "/")
proxy(w, r, proxy_path)
return
}
if f, err := os.Open(seg + "/junkrc"); err == nil {
// info, err := f.Stat()
s := bufio.NewScanner(f)
conf := JunkConf{}
for s.Scan() {
line := s.Text()
if len(line) > 2 && line[0:2] == "#;" {
note := line[2:]
i := strings.IndexByte(note, '=')
if i == -1 {
continue
}
key, value := note[:i], note[i+1:]
switch key {
case "proxy":
conf.Proxy = value
}
}
}
f.Close()
println("exec:", seg+"/junkrc")
cmd := exec.Command("./junkrc")
cmd.Dir = seg
// cmd.Stdout = os.Stdout
conf.Cmd = cmd
err = cmd.Start()
if err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte(err.Error()))
return
}
waits := 0
for {
_, err := http.Head(conf.Proxy)
if err != nil {
if cmd.ProcessState != nil {
cmd.Wait()
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("process exited early: " + cmd.ProcessState.String()))
return
} else {
waits++
time.Sleep(500 * time.Millisecond)
if waits > 10 {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("starting process: timeout"))
cmd.Process.Kill()
cmd.Wait()
return
}
continue
}
}
cmdmap[seg] = conf
break
}
w.Header().Add("Set-Cookie", "view="+seg+"; Path=/; SameSite=Strict; HttpOnly")
w.Header().Add("Location", p1)
w.WriteHeader(http.StatusFound)
// proxy_path := conf.Proxy + "/" + strings.Join(p[1:], "/")
// proxy(w, r, proxy_path)
return
}
fileserver.ServeHTTP(w, r)
})
lock := make(chan os.Signal, 1)
signal.Notify(lock, os.Interrupt, syscall.SIGTERM)
go func() {
println("started:", "http://"+junkaddr+"/")
http.ListenAndServe(junkaddr, nil)
}()
<-lock
for k, v := range cmdmap {
println("killing:", k)
v.Cmd.Process.Kill()
v.Cmd.Wait()
}
return ""
}