X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=src%2Ffoundation%2Fapi%2Frevel%2Futil.go;fp=src%2Ffoundation%2Fapi%2Frevel%2Futil.go;h=340c5995b65078cf392beb7e0fb9bc7c4b90f415;hb=1d1ee6961c93781e1187d8c7faa868da6b2f01f4;hp=0000000000000000000000000000000000000000;hpb=56dd5e0f2164b37b40ac1daa188ccc618b4cbd19;p=iec.git diff --git a/src/foundation/api/revel/util.go b/src/foundation/api/revel/util.go new file mode 100644 index 0000000..340c599 --- /dev/null +++ b/src/foundation/api/revel/util.go @@ -0,0 +1,280 @@ +// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved. +// Revel Framework source code and usage is governed by a MIT style +// license that can be found in the LICENSE file. + +package revel + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strings" + + "github.com/revel/config" +) + +const ( + // DefaultFileContentType Revel's default response content type + DefaultFileContentType = "application/octet-stream" +) + +var ( + cookieKeyValueParser = regexp.MustCompile("\x00([^:]*):([^\x00]*)\x00") + HdrForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") + HdrRealIP = http.CanonicalHeaderKey("X-Real-Ip") + utilLog = RevelLog.New("section", "util") + mimeConfig *config.Context +) + +// ExecutableTemplate adds some more methods to the default Template. +type ExecutableTemplate interface { + Execute(io.Writer, interface{}) error +} + +// ExecuteTemplate execute a template and returns the result as a string. +func ExecuteTemplate(tmpl ExecutableTemplate, data interface{}) string { + var b bytes.Buffer + if err := tmpl.Execute(&b, data); err != nil { + utilLog.Error("ExecuteTemplate: Execute failed", "error", err) + } + return b.String() +} + +// MustReadLines reads the lines of the given file. Panics in the case of error. +func MustReadLines(filename string) []string { + r, err := ReadLines(filename) + if err != nil { + panic(err) + } + return r +} + +// ReadLines reads the lines of the given file. Panics in the case of error. +func ReadLines(filename string) ([]string, error) { + dataBytes, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return strings.Split(string(dataBytes), "\n"), nil +} + +func ContainsString(list []string, target string) bool { + for _, el := range list { + if el == target { + return true + } + } + return false +} + +// FindMethod returns the reflect.Method, given a Receiver type and Func value. +func FindMethod(recvType reflect.Type, funcVal reflect.Value) *reflect.Method { + // It is not possible to get the name of the method from the Func. + // Instead, compare it to each method of the Controller. + for i := 0; i < recvType.NumMethod(); i++ { + method := recvType.Method(i) + if method.Func.Pointer() == funcVal.Pointer() { + return &method + } + } + return nil +} + +// ParseKeyValueCookie takes the raw (escaped) cookie value and parses out key values. +func ParseKeyValueCookie(val string, cb func(key, val string)) { + val, _ = url.QueryUnescape(val) + if matches := cookieKeyValueParser.FindAllStringSubmatch(val, -1); matches != nil { + for _, match := range matches { + cb(match[1], match[2]) + } + } +} + +// LoadMimeConfig load mime-types.conf on init. +func LoadMimeConfig() { + var err error + mimeConfig, err = config.LoadContext("mime-types.conf", ConfPaths) + if err != nil { + utilLog.Fatal("Failed to load mime type config:", "error", err) + } +} + +// ContentTypeByFilename returns a MIME content type based on the filename's extension. +// If no appropriate one is found, returns "application/octet-stream" by default. +// Additionally, specifies the charset as UTF-8 for text/* types. +func ContentTypeByFilename(filename string) string { + dot := strings.LastIndex(filename, ".") + if dot == -1 || dot+1 >= len(filename) { + return DefaultFileContentType + } + + extension := filename[dot+1:] + contentType := mimeConfig.StringDefault(extension, "") + if contentType == "" { + return DefaultFileContentType + } + + if strings.HasPrefix(contentType, "text/") { + return contentType + "; charset=utf-8" + } + + return contentType +} + +// DirExists returns true if the given path exists and is a directory. +func DirExists(filename string) bool { + fileInfo, err := os.Stat(filename) + return err == nil && fileInfo.IsDir() +} + +func FirstNonEmpty(strs ...string) string { + for _, str := range strs { + if len(str) > 0 { + return str + } + } + return "" +} + +// Equal is a helper for comparing value equality, following these rules: +// - Values with equivalent types are compared with reflect.DeepEqual +// - int, uint, and float values are compared without regard to the type width. +// for example, Equal(int32(5), int64(5)) == true +// - strings and byte slices are converted to strings before comparison. +// - else, return false. +func Equal(a, b interface{}) bool { + if reflect.TypeOf(a) == reflect.TypeOf(b) { + return reflect.DeepEqual(a, b) + } + switch a.(type) { + case int, int8, int16, int32, int64: + switch b.(type) { + case int, int8, int16, int32, int64: + return reflect.ValueOf(a).Int() == reflect.ValueOf(b).Int() + } + case uint, uint8, uint16, uint32, uint64: + switch b.(type) { + case uint, uint8, uint16, uint32, uint64: + return reflect.ValueOf(a).Uint() == reflect.ValueOf(b).Uint() + } + case float32, float64: + switch b.(type) { + case float32, float64: + return reflect.ValueOf(a).Float() == reflect.ValueOf(b).Float() + } + case string: + switch b.(type) { + case []byte: + return a.(string) == string(b.([]byte)) + } + case []byte: + switch b.(type) { + case string: + return b.(string) == string(a.([]byte)) + } + } + return false +} + +// ClientIP method returns client IP address from HTTP request. +// +// Note: Set property "app.behind.proxy" to true only if Revel is running +// behind proxy like nginx, haproxy, apache, etc. Otherwise +// you may get inaccurate Client IP address. Revel parses the +// IP address in the order of X-Forwarded-For, X-Real-IP. +// +// By default revel will get http.Request's RemoteAddr +func ClientIP(r *Request) string { + if Config.BoolDefault("app.behind.proxy", false) { + // Header X-Forwarded-For + if fwdFor := strings.TrimSpace(r.GetHttpHeader(HdrForwardedFor)); fwdFor != "" { + index := strings.Index(fwdFor, ",") + if index == -1 { + return fwdFor + } + return fwdFor[:index] + } + + // Header X-Real-Ip + if realIP := strings.TrimSpace(r.GetHttpHeader(HdrRealIP)); realIP != "" { + return realIP + } + } + + if remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { + return remoteAddr + } + + return "" +} + +// Walk method extends filepath.Walk to also follow symlinks. +// Always returns the path of the file or directory. +func Walk(root string, walkFn filepath.WalkFunc) error { + return fsWalk(root, root, walkFn) +} + +// createDir method creates nested directories if not exists +func createDir(path string) error { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + if err = os.MkdirAll(path, 0755); err != nil { + return fmt.Errorf("Failed to create directory '%v': %v", path, err) + } + } else { + return fmt.Errorf("Failed to create directory '%v': %v", path, err) + } + } + return nil +} + +func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error { + fsWalkFunc := func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + var name string + name, err = filepath.Rel(fname, path) + if err != nil { + return err + } + + path = filepath.Join(linkName, name) + + if err == nil && info.Mode()&os.ModeSymlink == os.ModeSymlink { + var symlinkPath string + symlinkPath, err = filepath.EvalSymlinks(path) + if err != nil { + return err + } + + // https://github.com/golang/go/blob/master/src/path/filepath/path.go#L392 + info, err = os.Lstat(symlinkPath) + + if err != nil { + return walkFn(path, info, err) + } + + if info.IsDir() { + return fsWalk(symlinkPath, path, walkFn) + } + } + + return walkFn(path, info, err) + } + err := filepath.Walk(fname, fsWalkFunc) + return err +} + +func init() { + OnAppStart(LoadMimeConfig) +}