diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-11-01 14:46:15 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-11-01 14:46:15 -0400 |
commit | c7eea383aeaf6748daf994e9e28e4d0c25350736 (patch) | |
tree | a0979cbd4e6be06385fd0340e050147fdacd6e35 /src/util |
initial commit
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/fd.go | 101 | ||||
-rw-r--r-- | src/util/http.go | 63 | ||||
-rw-r--r-- | src/util/template.go | 56 |
3 files changed, 220 insertions, 0 deletions
diff --git a/src/util/fd.go b/src/util/fd.go new file mode 100644 index 0000000..17c64cd --- /dev/null +++ b/src/util/fd.go @@ -0,0 +1,101 @@ +// Copyright 2015-2016 Luke Shumaker + +package util + +import ( + "net" + "os" + "strings" + "fmt" + "sync" + "strconv" + + sd "lukeshu.com/git/go/libsystemd/sd_daemon" +) + +var fdsLock sync.Mutex +var fds = map[int]*os.File{} +var sdFds = map[string]int{} + +func init() { + fds[0] = os.Stdin + fds[1] = os.Stdout + fds[2] = os.Stderr + fromSd := sd.ListenFds(true) + if fromSd == nil { + return + } + for i, file := range fromSd { + fds[i+3] = file + sdFds[file.Name()] = i+3 + } +} + +func FdNameToNum(name string) int { + switch name { + case "stdin": + return 0 + case "stdout": + return 1 + case "stderr": + return 2 + case "systemd": + if len(sdFds) == 0 { + return -1 + } + return 3 + default: + if n, err := strconv.Atoi(name); err == nil { + if n >= 0 { + return n + } + } else if strings.HasPrefix(name, "systemd:") { + name = strings.TrimPrefix(name, "systemd") + n, ok := sdFds[name] + if ok { + return n + } else if n, err := strconv.Atoi(name); err == nil && n < len(sdFds) { + return n+3 + } + } + return -1 + } +} + +func FdFile(fd int) *os.File { + fdsLock.Lock() + defer fdsLock.Unlock() + file, ok := fds[fd] + if ok { + return file + } + file = os.NewFile(uintptr(fd), fmt.Sprintf("/dev/fd/%d", fd)) + fds[fd] = file + return file +} + +func StreamListener(stype, saddr string) (net.Listener, error) { + switch stype { + case "fd": + return net.FileListener(FdFile(FdNameToNum(saddr))) + default: /* case "tcp", "tcp4", "tcp6", "unix", "unixpacket": */ + return net.Listen(stype, saddr) + } +} + +func PacketListener(stype, saddr string) (net.PacketConn, error) { + switch stype { + case "fd": + return net.FilePacketConn(FdFile(FdNameToNum(saddr))) + default: /* case "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unixgram": */ + return net.ListenPacket(stype, saddr) + } +} + +// For completeless, I might want to implement methods for each of +// these: +// - FIFO +// - Special +// - Netlink +// - MessageQueue +// - USBFunction diff --git a/src/util/http.go b/src/util/http.go new file mode 100644 index 0000000..6c216cf --- /dev/null +++ b/src/util/http.go @@ -0,0 +1,63 @@ +package util + +import ( + "bytes" + "fmt" + "log" + "net/http" + "runtime" + "strconv" +) + +type httpResponseBuffer struct { + inner http.ResponseWriter + buf bytes.Buffer + status int +} + +func (o *httpResponseBuffer) Header() http.Header { + return o.inner.Header() +} + +func (o *httpResponseBuffer) Write(data []byte) (int, error) { + return o.buf.Write(data) +} + +func (o *httpResponseBuffer) WriteHeader(status int) { + o.status = status +} + +type SaneHTTPHandler struct { + Inner http.Handler +} + +func (h SaneHTTPHandler) ServeHTTP(out http.ResponseWriter, in *http.Request) { + HTTPHandlerFuncWrapper(out, in, h.Inner.ServeHTTP) +} + +func HTTPHandlerFuncWrapper(out http.ResponseWriter, in *http.Request, fn http.HandlerFunc) { + buf := httpResponseBuffer{ + inner: out, + status: http.StatusOK, + } + ok := true + func() { + defer func() { + if r := recover(); r != nil { + const size = 64 << 10 + buf := make([]byte, size) + buf = buf[:runtime.Stack(buf, false)] + st := fmt.Sprintf("%[1]T(%#[1]v) => %[1]v\n\n%[2]s", r, string(buf)) + log.Printf("panic serving %v\n\n%s", in.URL, st) + http.Error(out, fmt.Sprintf("500 Internal Server Error:\n\n%s", st), 500) + ok = false + } + }() + fn(&buf, in) + }() + if ok { + out.Header().Set("Content-Length", strconv.Itoa(buf.buf.Len())) + out.WriteHeader(buf.status) + buf.buf.WriteTo(out) + } +} diff --git a/src/util/template.go b/src/util/template.go new file mode 100644 index 0000000..f42875a --- /dev/null +++ b/src/util/template.go @@ -0,0 +1,56 @@ +package util + +import ( + "path" + "text/template" + "time" +) + +func NewTemplate(filenames ...string) *template.Template { + return template.Must(template.New(path.Base(filenames[0])). + Funcs(template.FuncMap{ + // Form input helpers + "value": func(v interface{}) string { + if v == nil { + return "" + } + return "value=\"" + template.HTMLEscapeString(v.(string)) + "\"" + }, + "checked": func(v1 interface{}, v2 interface{}) string { + if v1 == nil || v2 == nil { + return "" + } + if v1 == v2 { + return "checked" + } + return "" + }, + "selected": func(v1 interface{}, v2 interface{}) string { + if v1 == nil || v2 == nil { + return "" + } + if v1 == v2 { + return "selected" + } + return "" + }, + // Form result helpers + "q": func(v interface{}) string { + if v == nil || v.(string) == "" { + return "<output class=none>none</output>" + } + return "<output><q>" + template.HTMLEscapeString(v.(string)) + "</q></output>" + }, + "have": func(v interface{}) bool { + return v != nil && v.(string) == "on" + }, + "date": func(v string) string { + t, err := time.Parse("2006-01-02", v) + if err != nil { + panic(err) + } + return t.Format("January 2, 2006") + }, + }). + ParseFiles(filenames...)) +} |