diff --git a/conf.go b/conf.go new file mode 100644 index 0000000..301533c --- /dev/null +++ b/conf.go @@ -0,0 +1,52 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" +) + +const CONFIG_LOG string = "/var/lib/gocustomurls/" + +type ConfigFile struct { + Mappings []struct { + Protocol string `json:"protocol"` + VanityUrl string `json:"vanity_url"` + RealUrl string `json:"real_url"` + } `json:"mappings"` +} + +// isFile - check if fp is a valid file +func isFile(fp string) bool { + info, err := os.Stat(fp) + if os.IsNotExist(err) || !info.Mode().IsRegular() { + return false + } + return true +} + +// load mapping file +func LoadFile() (ConfigFile, error) { + var mapping ConfigFile + dirname, err := os.UserConfigDir() + if err != nil { + return mapping, err + } + configFilePath := filepath.Join(dirname, "gocustomurls/config.json") + ok := isFile(configFilePath) + if !ok { + return mapping, fmt.Errorf("%s/gocustomurls/config.json file is not found", dirname) + } + configFile, err := os.Open(configFilePath) + if err != nil { + return mapping, err + } + defer configFile.Close() + + err = json.NewDecoder(configFile).Decode(&mapping) + if err != nil { + return mapping, err + } + return mapping, nil +} diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..b44afcb --- /dev/null +++ b/logger.go @@ -0,0 +1,148 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "os" + "strings" +) + +// some headers not worth logging +var ( + hdrsToNotLog = []string{ + "Accept-Language", + "Cache-Control", + "Cf-Ray", + "CF-Visitor", + "CF-Connecting-IP", + "Cdn-Loop", + "Cookie", + "Connection", + "Dnt", + "If-Modified-Since", + "Sec-Fetch-Dest", + "Sec-Ch-Ua-Mobile", + // "Sec-Ch-Ua", + "Sec-Ch-Ua-Platform", + "Sec-Fetch-Site", + "Sec-Fetch-Mode", + "Sec-Fetch-User", + "Upgrade-Insecure-Requests", + "X-Request-Start", + "X-Forwarded-For", + "X-Forwarded-Proto", + "X-Forwarded-Host", + } + hdrsToNotLogMap map[string]bool +) + +type LogFile struct { + handle *os.File + logger *log.Logger + path string +} + +type LogFileRec struct { + Method string `json:"method"` + IpAddr string `json:"ipAddr"` + Url string `json:"url"` +} + +func newFileLogger(path string) (*LogFile, error) { + f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) + if err != nil { + return nil, err + } + return &LogFile{ + handle: f, + logger: log.New(f, "", 0), + path: path, + }, nil +} + +func (f *LogFile) Close() error { + if f == nil { + return nil + } + err := f.handle.Close() + f.handle = nil + return err +} + +func extractFirstFragment(header *http.Header, headerName string) string { + s := header.Get(headerName) + if len(strings.TrimSpace(s)) == 0 { + return s + } + fragments := strings.Split(s, ",") + return strings.TrimSpace(fragments[0]) +} + +// Get Ip Address of the client +func extractIpAddress(r *http.Request) string { + var ipAddr string + if r == nil { + return "" + } + possibleIpHeaders := []string{"CF-Connecting-IP", "X-Real-Ip", "X-Forwarded-For"} + for _, header := range possibleIpHeaders { + ipAddr = extractFirstFragment(&r.Header, header) + if len(strings.TrimSpace(ipAddr)) != 0 { + return ipAddr + } + } + // pull ip from Request.RemoteAddr + if len(strings.TrimSpace(r.RemoteAddr)) != 0 { + index := strings.LastIndex(r.RemoteAddr, ";") + if index == -1 { + return r.RemoteAddr + } + ipAddr = r.RemoteAddr[:index] + } + return ipAddr +} + +func canSkipExtraHeaders(r *http.Request) bool { + ref := r.Header.Get("Referer") + if len(strings.TrimSpace(ref)) == 0 { + return false + } + return strings.Contains(ref, r.Host) +} + +func shouldLogHeader(s string) bool { + if hdrsToNotLogMap == nil { + hdrsToNotLogMap = map[string]bool{} + for _, h := range hdrsToNotLog { + h = strings.ToLower(h) + hdrsToNotLogMap[h] = true + } + } + s = strings.ToLower(s) + return !hdrsToNotLogMap[s] +} + +func (f *LogFile) WriteLog(r *http.Request) error { + if f == nil { + return nil + } + var rec = make(map[string]string) + rec["method"] = r.Method + rec["requestUri"] = r.RequestURI + rec["Host"] = r.Host + rec["ipAddr"] = extractIpAddress(r) + if !canSkipExtraHeaders(r) { + for key, val := range r.Header { + if shouldLogHeader(key) && len(val) > 0 { + rec[key] = val[0] + } + } + } + b, err := json.Marshal(rec) + if err != nil { + return err + } + f.logger.Println(string(b)) + return nil +}