Skip to content

Commit

Permalink
deglobalize variables; protect stats map with sync.Mutex
Browse files Browse the repository at this point in the history
  • Loading branch information
jmank88 committed Mar 22, 2018
1 parent 2bca5a2 commit d5d8135
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 33 deletions.
10 changes: 6 additions & 4 deletions handler.go
Expand Up @@ -7,15 +7,18 @@ import (
"log"
"net"
"net/http"
"sync"
"time"
)

type myTransport struct {
matcher
stats map[string]MonitoringPath
statsMu sync.RWMutex
}

type ModifiedRequest struct {
Path string
Method string
RemoteAddr string
}

Expand Down Expand Up @@ -48,7 +51,6 @@ func parseRequest(r *http.Request) ModifiedRequest {
}
return ModifiedRequest{
Path: path,
Method: r.Method,
RemoteAddr: ip,
}
}
Expand All @@ -58,7 +60,7 @@ func (t *myTransport) RoundTrip(request *http.Request) (*http.Response, error) {
start := time.Now()
parsedRequest := parseRequest(request)

if !MatchAnyRule(parsedRequest) {
if !t.MatchAnyRule(parsedRequest) {
log.Println("Not allowed:", parsedRequest.Path, " from IP: ", parsedRequest.RemoteAddr)
return &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString("You are not authorized to make this request")),
Expand All @@ -83,7 +85,7 @@ func (t *myTransport) RoundTrip(request *http.Request) (*http.Response, error) {
}, err
}
elapsed := time.Since(start)
updateStats(parsedRequest, elapsed)
t.updateStats(parsedRequest, elapsed)
log.Println("Response Time:", elapsed.Seconds(), " path: ", parsedRequest.Path, " from IP: ", parsedRequest.RemoteAddr)

return response, err
Expand Down
32 changes: 19 additions & 13 deletions main.go
Expand Up @@ -12,27 +12,38 @@ import (
type Prox struct {
target *url.URL
proxy *httputil.ReverseProxy
myTransport
}

func NewProxy(target string) *Prox {
func NewProxy(target string, m matcher) *Prox {
url, _ := url.Parse(target)

return &Prox{target: url, proxy: httputil.NewSingleHostReverseProxy(url)}
p := &Prox{target: url, proxy: httputil.NewSingleHostReverseProxy(url)}
p.stats = make(map[string]MonitoringPath)
p.matcher = m
p.proxy.Transport = &p.myTransport
return p
}

func (p *Prox) handle(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-rpc-proxy", "rpc-proxy")
p.proxy.Transport = &myTransport{}

p.proxy.ServeHTTP(w, r)
}

func (p *Prox) ServerStatus(w http.ResponseWriter, r *http.Request) {
stats, err := p.getStats()
if err != nil {
http.Error(w, "failed to get stats", http.StatusInternalServerError)
log.Println("Failed to get server stats:", err)
} else {
w.Write(stats)
}
}

var port *string
var redirecturl *string
var allowedPathes *string
var requestsPerMinuteLimit *int
var globalMap = make(map[string]MonitoringPath)

func main() {
const (
Expand Down Expand Up @@ -60,21 +71,16 @@ func main() {
log.Println("requests from IP per minute limited to :", *requestsPerMinuteLimit)

// filling matcher rules
err := AddMatcherRules(strings.Split(*allowedPathes, ","))
rules, err := newMatcher(strings.Split(*allowedPathes, ","))
if err != nil {
log.Println("Cannot parse list of allowed pathes", err)
}
// proxy
proxy := NewProxy(*redirecturl)
proxy := NewProxy(*redirecturl, rules)

http.HandleFunc("/rpc-proxy-server-status", ServerStatus)
http.HandleFunc("/rpc-proxy-server-status", proxy.ServerStatus)

// server redirection
http.HandleFunc("/", proxy.handle)
log.Fatal(http.ListenAndServe(":"+*port, nil))
}

func ServerStatus(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(getStats()))
return
}
15 changes: 8 additions & 7 deletions matcher.go
Expand Up @@ -4,28 +4,29 @@ import (
"regexp"
)

var paths []*regexp.Regexp
type matcher []*regexp.Regexp

func MatchAnyRule(request ModifiedRequest) bool {
func (m matcher) MatchAnyRule(request ModifiedRequest) bool {

if request.Path == "" {
return false
}
for _, matcher := range paths {
for _, matcher := range m {
if matcher.MatchString(request.Path) {
return true
}
}
return false
}

func AddMatcherRules(rules []string) error {
func newMatcher(rules []string) (matcher, error) {
var m matcher
for _, p := range rules {
compiled, err := regexp.Compile(p)
if err != nil {
return err
return nil, err
}
paths = append(paths, compiled)
m = append(m, compiled)
}
return nil
return m, nil
}
21 changes: 12 additions & 9 deletions stats.go
Expand Up @@ -2,7 +2,6 @@ package main

import (
"encoding/json"
"log"
"time"
)

Expand All @@ -13,27 +12,31 @@ type MonitoringPath struct {
AverageTime float64
}

func updateStats(parsedRequest ModifiedRequest, elapsed time.Duration) {
func (t *myTransport) updateStats(parsedRequest ModifiedRequest, elapsed time.Duration) {
key := parsedRequest.RemoteAddr + "-" + parsedRequest.Path
if val, ok := globalMap[key]; ok {
t.statsMu.Lock()
defer t.statsMu.Unlock()
if val, ok := t.stats[key]; ok {
val.Count = val.Count + 1
val.TotalDuration += elapsed.Seconds()
val.AverageTime = val.TotalDuration / val.Count
globalMap[key] = val
t.stats[key] = val
} else {
var m MonitoringPath
m.Path = parsedRequest.Path
m.Count = 1
m.TotalDuration = elapsed.Seconds()
m.AverageTime = m.TotalDuration / m.Count
globalMap[key] = m
t.stats[key] = m
}
}

func getStats() string {
b, err := json.MarshalIndent(globalMap, "", " ")
func (t *myTransport) getStats() ([]byte, error) {
t.statsMu.RLock()
defer t.statsMu.RUnlock()
b, err := json.MarshalIndent(t.stats, "", " ")
if err != nil {
log.Println("error:", err)
return nil, err
}
return string(b)
return b, nil
}

0 comments on commit d5d8135

Please sign in to comment.