From 4bcce8ddfd4a242069552dbe4e51bc091d202a42 Mon Sep 17 00:00:00 2001 From: Evan Chen Date: Tue, 19 Oct 2021 12:34:01 +0800 Subject: [PATCH] feat: #8, #9, #10 --- api.go | 26 ++++++------- netutil.go | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++ route.go | 2 +- util.go | 98 +++++++++++++++++++----------------------------- 4 files changed, 161 insertions(+), 73 deletions(-) create mode 100644 netutil.go diff --git a/api.go b/api.go index 0cbdcac..f4b8f5c 100644 --- a/api.go +++ b/api.go @@ -11,7 +11,7 @@ import ( func ListFiles(w http.ResponseWriter, r *http.Request) { data, err := json.Marshal(files) if err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } w.Write(data) @@ -21,12 +21,12 @@ func GetFile(w http.ResponseWriter, r *http.Request) { name := r.URL.Query().Get("name") file, ok := files[name] if name == "" || !ok { - HttpWriter(w, 404, "file not found") + MakeResponse(w, 404, []byte("file not found")) return } data, err := file.Read() if err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } response, err := json.Marshal(map[string]string{ @@ -36,7 +36,7 @@ func GetFile(w http.ResponseWriter, r *http.Request) { "data": string(data), }) if err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } w.Header().Set("Content-Type", "application/json") @@ -47,21 +47,21 @@ func PostFile(w http.ResponseWriter, r *http.Request) { data, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } f := configui.File{} if err := json.Unmarshal(data, &f); err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } file, ok := files[f.Alias] if !ok { - HttpWriter(w, 404, "file not found") + MakeResponse(w, 404, []byte("file not found")) return } if err := file.Write([]byte(f.Data)); err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } w.Write([]byte("ok")) @@ -71,12 +71,12 @@ func Apply(w http.ResponseWriter, r *http.Request) { name := r.URL.Query().Get("name") file, ok := files[name] if name == "" || !ok { - HttpWriter(w, 404, "file not found") + MakeResponse(w, 404, []byte("file not found")) return } result, err := file.Do() if err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } w.Write([]byte(result)) @@ -95,12 +95,12 @@ func LoadConfig(w http.ResponseWriter, r *http.Request) { data, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } ftmp, err := configui.ReadConfig(string(data)) if err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } files = configui.GetFileMap(ftmp) @@ -110,7 +110,7 @@ func LoadConfig(w http.ResponseWriter, r *http.Request) { func getConfigHandler(w http.ResponseWriter, r *http.Request) { data, err := GetConfig() if err != nil { - HttpWriter(w, 500, err.Error()) + AbortError(w, err) return } w.Write(data) diff --git a/netutil.go b/netutil.go new file mode 100644 index 0000000..597ec47 --- /dev/null +++ b/netutil.go @@ -0,0 +1,108 @@ +package main + +import ( + "bytes" + "log" + "net" + "net/http" + "strconv" + "strings" +) + +type ResponseWriter struct { + http.ResponseWriter + StatueCode int +} + +func (w *ResponseWriter) WriteHeader(statusCode int) { + if w.StatueCode != 0 { + return + } + w.StatueCode = statusCode + w.ResponseWriter.WriteHeader(statusCode) +} + +func (w *ResponseWriter) Write(body []byte) (int, error) { + if w.StatueCode == 0 { + w.WriteHeader(200) + } + return w.ResponseWriter.Write(body) +} + +func MakeResponse(w http.ResponseWriter, status int, body []byte) (int, error) { + w.WriteHeader(status) + return w.Write(body) +} + +func AbortError(w http.ResponseWriter, err interface{}) (int, error) { + switch v := err.(type) { + case int: + w.WriteHeader(v) + return w.Write([]byte(strconv.Itoa(v))) + case string: + w.WriteHeader(500) + return w.Write([]byte(v)) + case error: + w.WriteHeader(500) + return w.Write([]byte(v.Error())) + default: + w.WriteHeader(500) + return w.Write([]byte(strconv.Itoa(500))) + } +} + +func Catch(rw *ResponseWriter, r *http.Request) { + ex := recover() + if ex != nil { + AbortError(rw, r) + log.Printf("%s %s %d %s %s\n", GetIP(r), r.Method, rw.StatueCode, r.URL, r.Header.Get("User-Agent")) + } +} + +func Middleware(next http.Handler) http.Handler { + return http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + rw := &ResponseWriter{w, 0} + defer Catch(rw, r) + abort := false + if flagAllow != "" { + if !matchIPGlob(GetIP(r), flagAllow) { + MakeResponse(rw, 403, []byte("permission denyed")) + abort = true + } + } + if !abort { + next.ServeHTTP(rw, r) + } + log.Printf("%s %s %d %s %s\n", GetIP(r), r.Method, rw.StatueCode, r.URL, r.Header.Get("User-Agent")) + }, + ) +} + +func GetIP(r *http.Request) string { + ip := r.Header.Get("X-Real-Ip") + if ip == "" { + ips := r.Header.Get("X-Forwarded-For") + ipArr := strings.Split(ips, ",") + ip = strings.Trim(ipArr[len(ipArr)-1], " ") + } + if ip == "" { + var err error + ip, _, err = net.SplitHostPort(r.RemoteAddr) + if err != nil { + ip = r.RemoteAddr + } + } + return ip +} + +func Parse(w http.ResponseWriter, name string, data interface{}) error { + buf := &bytes.Buffer{} + err := tmpl.ExecuteTemplate(buf, "home", data) + if err != nil { + AbortError(w, err) + return err + } + _, err = w.Write(buf.Bytes()) + return err +} diff --git a/route.go b/route.go index 5e3f254..e3c124e 100644 --- a/route.go +++ b/route.go @@ -37,6 +37,6 @@ func setRoutes(mux *http.ServeMux) { Lang: "json", } - serve(w, "home", data) + Parse(w, "home", data) }) } diff --git a/util.go b/util.go index c0df496..c4e28f5 100644 --- a/util.go +++ b/util.go @@ -2,40 +2,53 @@ package main import ( "archive/tar" - "bytes" "compress/gzip" "io" - "log" - "net" - "net/http" "os" "strings" ) -func Middleware(next http.Handler) http.Handler { - return http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - defer func() { - r := recover() - if r != nil { - log.Println("panic", r) - w.WriteHeader(500) - } - }() - abort := false - if flagAllow != "" { - if GetIP(r) != flagAllow { - HttpWriter(w, 403, "permission denyed") - abort = true - } - } +func matchIPGlob(ip, pattern string) bool { + parts := strings.Split(pattern, ".") + seg := strings.Split(ip, ".") + for i, part := range parts { - if !abort { - next.ServeHTTP(w, r) + // normalize pattern to 3 digits + switch len(part) { + case 1: + if part == "*" { + part = "***" + } else { + part = "00" + part } - log.Printf("%s %s %s %s\n", GetIP(r), r.Method, r.URL, r.Header.Get("User-Agent")) - }, - ) + case 2: + if strings.HasPrefix(part, "*") { + part = "*" + part + } else if strings.HasSuffix(part, "*") { + part = part + "*" + } else { + part = "0" + part + } + } + + // normalize ip to 3 digits + switch len(seg[i]) { + case 1: + seg[i] = "00" + seg[i] + case 2: + seg[i] = "0" + seg[i] + } + + for j := range part { + if string(part[j]) == "*" { + continue + } + if part[j] != seg[i][j] { + return false + } + } + } + return true } func bundle(buf io.Writer, paths []string, flat bool) error { @@ -95,36 +108,3 @@ func bundle(buf io.Writer, paths []string, flat bool) error { return nil } - -func HttpWriter(w http.ResponseWriter, status int, errStr string) { - w.WriteHeader(status) - w.Write([]byte(errStr)) -} - -func GetIP(r *http.Request) string { - ip := r.Header.Get("X-Real-Ip") - if ip == "" { - ips := r.Header.Get("X-Forwarded-For") - ipArr := strings.Split(ips, ",") - ip = strings.Trim(ipArr[len(ipArr)-1], " ") - } - if ip == "" { - var err error - ip, _, err = net.SplitHostPort(r.RemoteAddr) - if err != nil { - ip = r.RemoteAddr - } - } - return ip -} - -func serve(w http.ResponseWriter, name string, data interface{}) error { - buf := &bytes.Buffer{} - err := tmpl.ExecuteTemplate(buf, "home", data) - if err != nil { - HttpWriter(w, 500, err.Error()) - return err - } - _, err = w.Write(buf.Bytes()) - return err -}