helpers.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. package helpers
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "encoding/json"
  6. "errors"
  7. "log"
  8. "net/http"
  9. "os"
  10. "regexp"
  11. "runtime"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/vladimirok5959/golang-server-sessions/session"
  16. )
  17. // func ClientIP(r *http.Request) string
  18. // func ClientIPs(r *http.Request) []string
  19. // func HandleAppStatus() http.Handler
  20. // func HandleFile(data, contentType string) http.Handler
  21. // func HandleImageGif(data string) http.Handler
  22. // func HandleImageJpeg(data string) http.Handler
  23. // func HandleImagePng(data string) http.Handler
  24. // func HandleTextCss(data string) http.Handler
  25. // func HandleTextJavaScript(data string) http.Handler
  26. // func HandleTextPlain(data string) http.Handler
  27. // func HandleTextXml(data string) http.Handler
  28. // func InArrayInt(num int, arr []int) bool
  29. // func InArrayInt64(num int64, arr []int64) bool
  30. // func InArrayStr(str string, arr []string) bool
  31. // func IntToStr(value int) string
  32. // func IntToStr64(value int64) string
  33. // func Md5Hash(str []byte) string
  34. // func MinifyHtmlCode(str string) string
  35. // func MinifyHtmlJsCode(str string) string
  36. // func RespondAsBadRequest(w http.ResponseWriter, r *http.Request, err error)
  37. // func RespondAsInternalServerError(w http.ResponseWriter, r *http.Request)
  38. // func RespondAsMethodNotAllowed(w http.ResponseWriter, r *http.Request)
  39. // func SessionStart(w http.ResponseWriter, r *http.Request) (*session.Session, error)
  40. // func SetLanguageCookie(w http.ResponseWriter, r *http.Request) error
  41. // func StrToInt(value string) int
  42. // func StrToInt64(value string) int64
  43. var mHtml = regexp.MustCompile(`>[\n\t\r]+<`)
  44. var mHtmlLeft = regexp.MustCompile(`>[\n\t\r]+`)
  45. var mHtmlRight = regexp.MustCompile(`[\n\t\r]+<`)
  46. var mScript = regexp.MustCompile(`<script>([^<]*)</script>`)
  47. var mScriptCommentsInline = regexp.MustCompile(`//.*\n`)
  48. var mScriptCommentsMultiline = regexp.MustCompile(`/\*[^*]*\*/`)
  49. var mScriptLine = regexp.MustCompile(`[\n\t\r]+`)
  50. var mScriptEqual = regexp.MustCompile(`[\n\t\r\s]+=[\n\t\r\s]+`)
  51. var mScriptDots = regexp.MustCompile(`:[\n\t\r\s]+"`)
  52. var mScriptFuncs = regexp.MustCompile(`\)[\n\t\r\s]+{`)
  53. func ClientIP(r *http.Request) string {
  54. ips := ClientIPs(r)
  55. if len(ips) >= 1 {
  56. return ips[0]
  57. }
  58. return ""
  59. }
  60. func ClientIPs(r *http.Request) []string {
  61. ra := r.RemoteAddr
  62. if xff := strings.Trim(r.Header.Get("X-Forwarded-For"), " "); xff != "" {
  63. ra = strings.Join([]string{xff, ra}, ",")
  64. }
  65. res := []string{}
  66. ips := strings.Split(ra, ",")
  67. for _, ip := range ips {
  68. i := strings.LastIndex(ip, ":")
  69. if i < 0 {
  70. res = append(res, strings.Trim(ip, " "))
  71. } else {
  72. res = append(res, strings.Trim(string([]rune(ip)[:i]), " "))
  73. }
  74. }
  75. return res
  76. }
  77. func HandleAppStatus() http.Handler {
  78. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  79. if r.Method != http.MethodGet {
  80. RespondAsMethodNotAllowed(w, r)
  81. return
  82. }
  83. var m runtime.MemStats
  84. runtime.ReadMemStats(&m)
  85. type respMemory struct {
  86. Alloc uint64 `json:"alloc"`
  87. NumGC uint32 `json:"num_gc"`
  88. Sys uint64 `json:"sys"`
  89. TotalAlloc uint64 `json:"total_alloc"`
  90. }
  91. type respRoot struct {
  92. Memory respMemory `json:"memory"`
  93. Routines int `json:"routines"`
  94. }
  95. resp := respRoot{
  96. Memory: respMemory{
  97. Alloc: m.Alloc,
  98. NumGC: m.NumGC,
  99. Sys: m.Sys,
  100. TotalAlloc: m.TotalAlloc,
  101. },
  102. Routines: runtime.NumGoroutine(),
  103. }
  104. j, err := json.Marshal(resp)
  105. if err != nil {
  106. RespondAsBadRequest(w, r, err)
  107. return
  108. }
  109. w.Header().Set("Content-Type", "application/json")
  110. if _, err := w.Write(j); err != nil {
  111. log.Printf("%s\n", err.Error())
  112. }
  113. })
  114. }
  115. func HandleFile(data, contentType string) http.Handler {
  116. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  117. if r.Method != http.MethodGet {
  118. RespondAsMethodNotAllowed(w, r)
  119. return
  120. }
  121. w.Header().Set("Content-Type", contentType)
  122. if _, err := w.Write([]byte(data)); err != nil {
  123. log.Printf("%s\n", err.Error())
  124. }
  125. })
  126. }
  127. func HandleImageGif(data string) http.Handler {
  128. return HandleFile(data, "image/gif")
  129. }
  130. func HandleImageJpeg(data string) http.Handler {
  131. return HandleFile(data, "image/jpeg")
  132. }
  133. func HandleImagePng(data string) http.Handler {
  134. return HandleFile(data, "image/png")
  135. }
  136. func HandleTextCss(data string) http.Handler {
  137. return HandleFile(data, "text/css")
  138. }
  139. func HandleTextJavaScript(data string) http.Handler {
  140. return HandleFile(data, "text/javascript")
  141. }
  142. func HandleTextPlain(data string) http.Handler {
  143. return HandleFile(data, "text/plain")
  144. }
  145. func InArrayInt(num int, arr []int) bool {
  146. for _, v := range arr {
  147. if num == v {
  148. return true
  149. }
  150. }
  151. return false
  152. }
  153. func InArrayInt64(num int64, arr []int64) bool {
  154. for _, v := range arr {
  155. if num == v {
  156. return true
  157. }
  158. }
  159. return false
  160. }
  161. func InArrayStr(str string, arr []string) bool {
  162. for _, v := range arr {
  163. if str == v {
  164. return true
  165. }
  166. }
  167. return false
  168. }
  169. func HandleTextXml(data string) http.Handler {
  170. return HandleFile(data, "text/xml")
  171. }
  172. func IntToStr(value int) string {
  173. return strconv.FormatInt(int64(value), 10)
  174. }
  175. func IntToStr64(value int64) string {
  176. return strconv.FormatInt(value, 10)
  177. }
  178. func Md5Hash(str []byte) string {
  179. h := md5.New()
  180. if _, err := h.Write(str); err != nil {
  181. return ""
  182. }
  183. return hex.EncodeToString(h.Sum(nil))
  184. }
  185. func MinifyHtmlCode(str string) string {
  186. str = MinifyHtmlJsCode(str)
  187. str = mHtml.ReplaceAllString(str, "><")
  188. str = mHtmlLeft.ReplaceAllString(str, ">")
  189. str = mHtmlRight.ReplaceAllString(str, "<")
  190. return str
  191. }
  192. func MinifyHtmlJsCode(str string) string {
  193. return mScript.ReplaceAllStringFunc(str, func(str string) string {
  194. str = strings.TrimPrefix(str, "<script>")
  195. str = strings.TrimSuffix(str, "</script>")
  196. str = mScriptCommentsInline.ReplaceAllString(str, "")
  197. str = mScriptCommentsMultiline.ReplaceAllString(str, "")
  198. str = mScriptLine.ReplaceAllString(str, "")
  199. str = mScriptEqual.ReplaceAllString(str, "=")
  200. str = mScriptDots.ReplaceAllString(str, ":\"")
  201. str = mScriptFuncs.ReplaceAllString(str, "){")
  202. return `<script>` + str + `</script>`
  203. })
  204. }
  205. func RespondAsBadRequest(w http.ResponseWriter, r *http.Request, err error) {
  206. if err != nil {
  207. log.Printf("%s\n", err.Error())
  208. w.Header().Set("Content-Type", "application/json")
  209. w.WriteHeader(http.StatusBadRequest)
  210. type Resp struct {
  211. Error string `json:"error"`
  212. }
  213. resp := Resp{
  214. Error: err.Error(),
  215. }
  216. j, err := json.Marshal(resp)
  217. if err != nil {
  218. log.Printf("%s\n", err.Error())
  219. return
  220. }
  221. if _, err := w.Write(j); err != nil {
  222. log.Printf("%s\n", err.Error())
  223. }
  224. } else {
  225. w.WriteHeader(http.StatusBadRequest)
  226. }
  227. }
  228. func RespondAsInternalServerError(w http.ResponseWriter, r *http.Request) {
  229. w.WriteHeader(http.StatusInternalServerError)
  230. }
  231. func RespondAsMethodNotAllowed(w http.ResponseWriter, r *http.Request) {
  232. w.WriteHeader(http.StatusMethodNotAllowed)
  233. }
  234. // Example:
  235. //
  236. // sess, err := helpers.SessionStart(w, r)
  237. //
  238. // if err != nil && !errors.Is(err, os.ErrNotExist) {
  239. //
  240. // helpers.RespondAsBadRequest(w, r, err)
  241. // return
  242. //
  243. // }
  244. //
  245. // defer sess.Close()
  246. func SessionStart(w http.ResponseWriter, r *http.Request) (*session.Session, error) {
  247. sess, err := session.New(w, r, "/tmp")
  248. if err != nil && !errors.Is(err, os.ErrNotExist) {
  249. log.Printf("%s\n", err.Error())
  250. }
  251. return sess, err
  252. }
  253. // Example:
  254. //
  255. // if err = r.ParseForm(); err != nil {
  256. // helpers.RespondAsBadRequest(w, r, err)
  257. // return
  258. // }
  259. //
  260. // if err = helpers.SetLanguageCookie(w, r); err != nil {
  261. // helpers.RespondAsBadRequest(w, r, err)
  262. // return
  263. // }
  264. func SetLanguageCookie(w http.ResponseWriter, r *http.Request) error {
  265. var clang string
  266. if c, err := r.Cookie("lang"); err == nil {
  267. clang = c.Value
  268. }
  269. lang := r.FormValue("lang")
  270. if lang != "" && lang != clang {
  271. http.SetCookie(w, &http.Cookie{
  272. Expires: time.Now().Add(365 * 24 * time.Hour),
  273. HttpOnly: true,
  274. Name: "lang",
  275. Path: "/",
  276. Value: lang,
  277. })
  278. }
  279. return nil
  280. }
  281. func StrToInt(value string) int {
  282. if v, err := strconv.Atoi(value); err == nil {
  283. return v
  284. }
  285. return 0
  286. }
  287. func StrToInt64(value string) int64 {
  288. if v, err := strconv.ParseInt(value, 10, 64); err == nil {
  289. return v
  290. }
  291. return 0
  292. }
  293. // -----------------------------------------------------------------------------
  294. // For funcs which write some data to http.ResponseWriter
  295. //
  296. // Example: w = NewFakeResponseWriter()
  297. //
  298. // w.Body, w.Headers, w.StatusCode
  299. type FakeResponseWriter struct {
  300. Body []byte
  301. Headers http.Header
  302. StatusCode int
  303. }
  304. func (w *FakeResponseWriter) Header() http.Header {
  305. return w.Headers
  306. }
  307. func (w *FakeResponseWriter) Write(b []byte) (int, error) {
  308. w.Body = append(w.Body, b...)
  309. return len(b), nil
  310. }
  311. func (w *FakeResponseWriter) WriteHeader(statusCode int) {
  312. w.StatusCode = statusCode
  313. }
  314. // Create fake http.ResponseWriter for using in tests
  315. func NewFakeResponseWriter() *FakeResponseWriter {
  316. return &FakeResponseWriter{
  317. Body: []byte{},
  318. Headers: http.Header{},
  319. StatusCode: http.StatusOK,
  320. }
  321. }