github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/gate/common/request.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package common 20 21 import ( 22 "fmt" 23 "net/http" 24 "net/url" 25 "regexp" 26 ) 27 28 // HTTPRequest is a serializable version of http.Request ( with only usefull fields ) 29 type HTTPRequest struct { 30 Method string 31 URL string 32 Header map[string][]string 33 ContentLength int64 34 WS bool 35 } 36 37 // SerializeHTTPRequest create a new HTTPRequest from a http.Request 38 func SerializeHTTPRequest(req *http.Request) (r *HTTPRequest) { 39 r = &HTTPRequest{ 40 URL: req.URL.String(), 41 Method: req.Method, 42 Header: req.Header, 43 ContentLength: req.ContentLength, 44 } 45 return 46 } 47 48 // UnserializeHTTPRequest create a new http.Request from a HTTPRequest 49 func UnserializeHTTPRequest(req *HTTPRequest) (r *http.Request, err error) { 50 r = new(http.Request) 51 r.Method = req.Method 52 r.URL, err = url.Parse(req.URL) 53 if err != nil { 54 return 55 } 56 r.Header = req.Header 57 r.ContentLength = req.ContentLength 58 return 59 } 60 61 // Rule match HTTP requests to allow / deny access 62 type Rule struct { 63 Method string 64 URL string 65 Headers map[string]string 66 67 methodRegex *regexp.Regexp 68 urlRegex *regexp.Regexp 69 headersRegex map[string]*regexp.Regexp 70 } 71 72 // NewRule creates a new Rule 73 func NewRule(method string, url string, headers map[string]string) (rule *Rule, err error) { 74 rule = new(Rule) 75 rule.Method = method 76 rule.URL = url 77 if headers != nil { 78 rule.Headers = headers 79 } else { 80 rule.Headers = make(map[string]string) 81 } 82 err = rule.Compile() 83 return 84 } 85 86 // Compile the regular expressions 87 func (rule *Rule) Compile() (err error) { 88 if rule.Method != "" { 89 rule.methodRegex, err = regexp.Compile(rule.Method) 90 if err != nil { 91 return 92 } 93 } 94 if rule.URL != "" { 95 rule.urlRegex, err = regexp.Compile(rule.URL) 96 if err != nil { 97 return 98 } 99 } 100 rule.headersRegex = make(map[string]*regexp.Regexp) 101 for header, regexStr := range rule.Headers { 102 var regex *regexp.Regexp 103 regex, err = regexp.Compile(regexStr) 104 if err != nil { 105 return 106 } 107 rule.headersRegex[header] = regex 108 } 109 return 110 } 111 112 // Match returns true if the http.Request matches the Rule 113 func (rule *Rule) Match(req *http.Request) bool { 114 if rule.methodRegex != nil && !rule.methodRegex.MatchString(req.Method) { 115 return false 116 } 117 if rule.urlRegex != nil && !rule.urlRegex.MatchString(req.URL.String()) { 118 return false 119 } 120 121 for headerName, regex := range rule.headersRegex { 122 if !regex.MatchString(req.Header.Get(headerName)) { 123 return false 124 } 125 } 126 127 return true 128 } 129 130 func (rule *Rule) String() string { 131 return fmt.Sprintf("%s %s %v", rule.Method, rule.URL, rule.Headers) 132 }