-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.go
160 lines (130 loc) · 4.07 KB
/
main.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package main
import (
"net/http"
"net/http/httputil"
"net/url"
"os"
"time"
"flag"
"fmt"
stdlog "log"
"github.com/Dynom/proxima/handlers"
"github.com/go-kit/kit/log"
"github.com/juju/ratelimit"
)
var (
allowedHosts argumentList
allowedImaginaryParams argumentList
allowedImaginaryActions argumentList
imaginaryURL string
pathSegmentToStrip string
listenPort int64
bucketRate float64
bucketSize int64
Version = "dev"
)
func init() {
flag.Var(&allowedHosts, "allow-hosts", "Repeatable flag (or a comma-separated list) for hosts to allow for the URL parameter (e.g. \"d2dktr6aauwgqs.cloudfront.net\")")
flag.Var(&allowedImaginaryParams, "allowed-params", "A comma separated list of parameters allows to be sent upstream. If empty, everything is allowed.")
flag.Var(&allowedImaginaryActions, "allowed-actions", "A comma separated list of actions allows to be sent upstream. If empty, everything is allowed.")
flag.StringVar(&imaginaryURL, "imaginary-url", "http://localhost:9000", "URL to imaginary (default: http://localhost:9000)")
flag.Int64Var(&listenPort, "listen-port", 8080, "Port to listen on")
flag.Float64Var(&bucketRate, "bucket-rate", 20, "Rate limiter bucket fill rate (req/s)")
flag.Int64Var(&bucketSize, "bucket-size", 500, "Rate limiter bucket size (burst capacity)")
flag.StringVar(&pathSegmentToStrip, "root-path-strip", "", "A section of the (left most) path to strip (e.g.: \"/static\"). Start with a /.")
}
func main() {
flag.Parse()
logger := log.With(
log.NewLogfmtLogger(os.Stderr),
"ts", log.DefaultTimestampUTC,
)
logger.Log(
"msg", "Starting.",
"version", Version,
"allowed_hosts", allowedHosts.PrettyString(),
"allowed_params", allowedImaginaryParams.PrettyString(),
"allowed_actions", allowedImaginaryActions.PrettyString(),
"path_to_strip", pathSegmentToStrip,
"imaginary_backend", imaginaryURL,
)
rURL, err := url.Parse(imaginaryURL)
if err != nil {
panic(err)
}
rlBucket := ratelimit.NewBucketWithRate(bucketRate, bucketSize)
proxy := newProxy(logger, rURL)
s := &http.Server{
Addr: fmt.Sprintf(":%d", listenPort),
Handler: decorateHandler(logger, proxy, rlBucket),
ReadHeaderTimeout: 2 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
func newProxy(l log.Logger, backend *url.URL) *httputil.ReverseProxy {
proxy := httputil.NewSingleHostReverseProxy(backend)
proxy.ErrorLog = stdlog.New(log.NewStdlibAdapter(l), "", stdlog.LstdFlags)
proxy.Transport = &http.Transport{
DisableCompression: true,
DisableKeepAlives: false,
IdleConnTimeout: 5 * time.Minute,
MaxIdleConns: 10000,
MaxIdleConnsPerHost: 10000,
ResponseHeaderTimeout: 10 * time.Second,
}
return proxy
}
type httpHandler func(h http.Handler) http.Handler
func decorateHandler(l log.Logger, handler http.Handler, b *ratelimit.Bucket) http.Handler {
decorators := []httpHandler{}
if len(allowedHosts) > 0 {
decorators = append(
decorators,
handlers.NewValidateURLParameter(
l,
allowedHosts,
))
}
if len(allowedImaginaryParams) > 0 {
decorators = append(
decorators,
handlers.NewAllowedParams(
l,
allowedImaginaryParams,
))
}
if len(allowedImaginaryActions) > 0 {
decorators = append(
decorators,
handlers.NewAllowedActions(
l,
allowedImaginaryActions,
))
}
if pathSegmentToStrip != "" {
decorators = append(
decorators,
handlers.NewPathStrip(
l,
pathSegmentToStrip,
))
}
// Defining early needed handlers last
decorators = append(
decorators,
// Defining "health" end-points.
handlers.NewHTTPStatusPaths(l, []string{"/health", "/"}, http.StatusOK),
// Ignoring common foo requests
handlers.NewHTTPStatusPaths(l, []string{"/favicon", "/favicon.ico"}, http.StatusNotFound),
handlers.NewRequestLogger(l),
handlers.NewRateLimitHandler(l, b),
)
for _, d := range decorators {
handler = d(handler)
}
return handler
}