1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
21 "github.com/revel/config"
25 // DefaultFileContentType Revel's default response content type
26 DefaultFileContentType = "application/octet-stream"
30 cookieKeyValueParser = regexp.MustCompile("\x00([^:]*):([^\x00]*)\x00")
31 HdrForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
32 HdrRealIP = http.CanonicalHeaderKey("X-Real-Ip")
33 utilLog = RevelLog.New("section", "util")
34 mimeConfig *config.Context
37 // ExecutableTemplate adds some more methods to the default Template.
38 type ExecutableTemplate interface {
39 Execute(io.Writer, interface{}) error
42 // ExecuteTemplate execute a template and returns the result as a string.
43 func ExecuteTemplate(tmpl ExecutableTemplate, data interface{}) string {
45 if err := tmpl.Execute(&b, data); err != nil {
46 utilLog.Error("ExecuteTemplate: Execute failed", "error", err)
51 // MustReadLines reads the lines of the given file. Panics in the case of error.
52 func MustReadLines(filename string) []string {
53 r, err := ReadLines(filename)
60 // ReadLines reads the lines of the given file. Panics in the case of error.
61 func ReadLines(filename string) ([]string, error) {
62 dataBytes, err := ioutil.ReadFile(filename)
66 return strings.Split(string(dataBytes), "\n"), nil
69 func ContainsString(list []string, target string) bool {
70 for _, el := range list {
78 // FindMethod returns the reflect.Method, given a Receiver type and Func value.
79 func FindMethod(recvType reflect.Type, funcVal reflect.Value) *reflect.Method {
80 // It is not possible to get the name of the method from the Func.
81 // Instead, compare it to each method of the Controller.
82 for i := 0; i < recvType.NumMethod(); i++ {
83 method := recvType.Method(i)
84 if method.Func.Pointer() == funcVal.Pointer() {
91 // ParseKeyValueCookie takes the raw (escaped) cookie value and parses out key values.
92 func ParseKeyValueCookie(val string, cb func(key, val string)) {
93 val, _ = url.QueryUnescape(val)
94 if matches := cookieKeyValueParser.FindAllStringSubmatch(val, -1); matches != nil {
95 for _, match := range matches {
96 cb(match[1], match[2])
101 // LoadMimeConfig load mime-types.conf on init.
102 func LoadMimeConfig() {
104 mimeConfig, err = config.LoadContext("mime-types.conf", ConfPaths)
106 utilLog.Fatal("Failed to load mime type config:", "error", err)
110 // ContentTypeByFilename returns a MIME content type based on the filename's extension.
111 // If no appropriate one is found, returns "application/octet-stream" by default.
112 // Additionally, specifies the charset as UTF-8 for text/* types.
113 func ContentTypeByFilename(filename string) string {
114 dot := strings.LastIndex(filename, ".")
115 if dot == -1 || dot+1 >= len(filename) {
116 return DefaultFileContentType
119 extension := filename[dot+1:]
120 contentType := mimeConfig.StringDefault(extension, "")
121 if contentType == "" {
122 return DefaultFileContentType
125 if strings.HasPrefix(contentType, "text/") {
126 return contentType + "; charset=utf-8"
132 // DirExists returns true if the given path exists and is a directory.
133 func DirExists(filename string) bool {
134 fileInfo, err := os.Stat(filename)
135 return err == nil && fileInfo.IsDir()
138 func FirstNonEmpty(strs ...string) string {
139 for _, str := range strs {
147 // Equal is a helper for comparing value equality, following these rules:
148 // - Values with equivalent types are compared with reflect.DeepEqual
149 // - int, uint, and float values are compared without regard to the type width.
150 // for example, Equal(int32(5), int64(5)) == true
151 // - strings and byte slices are converted to strings before comparison.
152 // - else, return false.
153 func Equal(a, b interface{}) bool {
154 if reflect.TypeOf(a) == reflect.TypeOf(b) {
155 return reflect.DeepEqual(a, b)
158 case int, int8, int16, int32, int64:
160 case int, int8, int16, int32, int64:
161 return reflect.ValueOf(a).Int() == reflect.ValueOf(b).Int()
163 case uint, uint8, uint16, uint32, uint64:
165 case uint, uint8, uint16, uint32, uint64:
166 return reflect.ValueOf(a).Uint() == reflect.ValueOf(b).Uint()
168 case float32, float64:
170 case float32, float64:
171 return reflect.ValueOf(a).Float() == reflect.ValueOf(b).Float()
176 return a.(string) == string(b.([]byte))
181 return b.(string) == string(a.([]byte))
187 // ClientIP method returns client IP address from HTTP request.
189 // Note: Set property "app.behind.proxy" to true only if Revel is running
190 // behind proxy like nginx, haproxy, apache, etc. Otherwise
191 // you may get inaccurate Client IP address. Revel parses the
192 // IP address in the order of X-Forwarded-For, X-Real-IP.
194 // By default revel will get http.Request's RemoteAddr
195 func ClientIP(r *Request) string {
196 if Config.BoolDefault("app.behind.proxy", false) {
197 // Header X-Forwarded-For
198 if fwdFor := strings.TrimSpace(r.GetHttpHeader(HdrForwardedFor)); fwdFor != "" {
199 index := strings.Index(fwdFor, ",")
203 return fwdFor[:index]
207 if realIP := strings.TrimSpace(r.GetHttpHeader(HdrRealIP)); realIP != "" {
212 if remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
219 // Walk method extends filepath.Walk to also follow symlinks.
220 // Always returns the path of the file or directory.
221 func Walk(root string, walkFn filepath.WalkFunc) error {
222 return fsWalk(root, root, walkFn)
225 // createDir method creates nested directories if not exists
226 func createDir(path string) error {
227 if _, err := os.Stat(path); err != nil {
228 if os.IsNotExist(err) {
229 if err = os.MkdirAll(path, 0755); err != nil {
230 return fmt.Errorf("Failed to create directory '%v': %v", path, err)
233 return fmt.Errorf("Failed to create directory '%v': %v", path, err)
239 func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
240 fsWalkFunc := func(path string, info os.FileInfo, err error) error {
246 name, err = filepath.Rel(fname, path)
251 path = filepath.Join(linkName, name)
253 if err == nil && info.Mode()&os.ModeSymlink == os.ModeSymlink {
254 var symlinkPath string
255 symlinkPath, err = filepath.EvalSymlinks(path)
260 // https://github.com/golang/go/blob/master/src/path/filepath/path.go#L392
261 info, err = os.Lstat(symlinkPath)
264 return walkFn(path, info, err)
268 return fsWalk(symlinkPath, path, walkFn)
272 return walkFn(path, info, err)
274 err := filepath.Walk(fname, fsWalkFunc)
279 OnAppStart(LoadMimeConfig)