/
util.go
141 lines (127 loc) · 3.75 KB
/
util.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm)
// Source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package util
import (
"errors"
"html/template"
"io"
"mime"
"net/http"
"net/url"
"path/filepath"
"reflect"
"strings"
"aahframe.work/ahttp"
)
var (
errSeeker = errors.New("static: seeker can't seek")
)
// IsValidTimeUnit method to check supported time unit suffixes.
// If supported returns true otherwise false.
func IsValidTimeUnit(str string, units ...string) bool {
for _, v := range units {
if strings.HasSuffix(str, v) {
return true
}
}
return false
}
// DetectFileContentType method to identify the static file content-type.
// It's similar to http.DetectContentType but has windows OS and corner case
// covered.
func DetectFileContentType(file string, content io.ReadSeeker) (string, error) {
ctype := MimeTypeByExtension(filepath.Ext(file))
if ctype == "" {
// read a chunk to decide between utf-8 text and binary
// only 512 bytes expected by `http.DetectContentType`
var buf [512]byte
n, _ := io.ReadFull(content, buf[:]) // #nosec
ctype = http.DetectContentType(buf[:n])
// rewind to output whole file
if _, err := content.Seek(0, io.SeekStart); err != nil {
return "", errSeeker
}
}
return ctype, nil
}
// MimeTypeByExtension method to get MIME info by file extension with corner case
// covered since mime.TypeByExtension behaves wired on windows for `.js` and `.css`,
// it better to have some basic measure
func MimeTypeByExtension(ext string) string {
switch ext {
case ".html", ".htm":
return ahttp.ContentTypeHTML.String()
case ".css":
return ahttp.ContentTypeCSSText.String()
case ".js":
return ahttp.ContentTypeJavascript.String()
case ".json":
return ahttp.ContentTypeJSON.String()
case ".xml":
return ahttp.ContentTypeXML.String()
case ".txt", ".text":
return ahttp.ContentTypePlainText.String()
default:
return mime.TypeByExtension(ext)
}
}
// FuncEqual method to compare to function callback interface data. In effect
// comparing the pointers of the indirect layer. Read more about the
// representation of functions here: http://golang.org/s/go11func
func FuncEqual(a, b interface{}) bool {
av := reflect.ValueOf(&a).Elem()
bv := reflect.ValueOf(&b).Elem()
return av.InterfaceData() == bv.InterfaceData()
}
// OnlyMIME method to strip everything after `;` from the content type value.
func OnlyMIME(ct string) string {
if idx := strings.IndexByte(ct, ';'); idx > 0 {
return ct[:idx]
}
return ct
}
// AddQueryString method to add the given query string key value pair appropriately
// to the given URL string.
func AddQueryString(u, k, v string) string {
v = url.QueryEscape(v)
if len(u) == 0 {
return "?" + k + "=" + v
}
if idx := strings.IndexByte(u, '?'); idx == -1 {
return u + "?" + k + "=" + v
}
return u + "&" + k + "=" + v
}
// SanitizeValue method to sanatizes type `string`, rest we can't do any.
// It's a user responbility.
func SanitizeValue(value interface{}) interface{} {
switch v := value.(type) {
case string:
return template.HTMLEscapeString(v)
default:
return v
}
}
// IsGzipWorthForFile method to decide whether gzipping file content it worth to do by
// checking file extension. If its worth to do it returns true otherwise false.
func IsGzipWorthForFile(name string) bool {
switch filepath.Ext(name) {
case ".css", ".js", ".html", ".htm", ".json", ".ico", ".svg",
".eot", ".ttf", ".otf", ".xml", ".rss", ".txt", ".csv":
return true
default:
return false
}
}
// FirstNonEmpty method returns the first non-empty string from given var args
// otherwise empty string.
func FirstNonEmpty(values ...string) string {
for _, v := range values {
v = strings.TrimSpace(v)
if len(v) > 0 {
return v
}
}
return ""
}