Skip to content

Commit

Permalink
Initial version.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dynom committed Apr 23, 2017
1 parent 10617eb commit 56bb1d2
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 0 deletions.
19 changes: 19 additions & 0 deletions handlers/favicon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package handlers

import (
"net/http"
"strings"
)

func NewIgnoreFaviconRequests() func(h http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.RequestURI, "/favicon") {
http.NotFound(w, r)
return
}

h.ServeHTTP(w, r)
})
}
}
23 changes: 23 additions & 0 deletions handlers/ratelimiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package handlers

import (
"net/http"
"time"

"github.com/go-kit/kit/log"
"github.com/juju/ratelimit"
)

func NewRateLimitHandler(b *ratelimit.Bucket, l log.Logger) func(h http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
d := b.Take(1)
if d > 0 {
l.Log("msg", "Rate limiting", "delay", d)
time.Sleep(d)
}

h.ServeHTTP(w, r)
})
}
}
43 changes: 43 additions & 0 deletions handlers/urlparameters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package handlers

import (
"net/http"
"net/url"

"github.com/go-kit/kit/log"
)

func NewValidateURLParameter(l log.Logger, allowedHosts []string) func(h http.Handler) http.Handler {
var hosts = make(map[string]bool, len(allowedHosts))
for _, host := range allowedHosts {
hosts[host] = true
}

return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
queryURL := r.URL.Query().Get("url")
if !isValidQueryURL(queryURL, hosts) {
l.Log("error", "domain not registered", "QS", r.URL.RawQuery, "URL", queryURL)
http.Error(w, "Unregisterd domain", http.StatusNotAcceptable)
return
}

h.ServeHTTP(w, r)
})
}
}

func isValidQueryURL(i string, hosts map[string]bool) bool {
if i == "" || len(i) > 2048 {
return false
}

qURL, err := url.Parse(i)
if err != nil {
return false
}

_, exists := hosts[qURL.Host]
return exists

}
110 changes: 110 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package main

import (
"net/http"
"net/http/httputil"
"net/url"
"os"
"time"

"strings"

"flag"

"fmt"

"github.com/Pimmr/proxima/handlers"
"github.com/go-kit/kit/log"
"github.com/juju/ratelimit"
)

var (
allowedHosts argumentList
imaginaryURL string
listenPort int64
bucketRate float64
bucketSize int64

Version = "dev"
logger = log.With(
log.NewLogfmtLogger(os.Stderr),
"ts", log.DefaultTimestampUTC,
"caller", log.DefaultCaller,
)
)

type argumentList []string

func (l argumentList) String() string {
return strings.Join(l, ",")
}

func (l *argumentList) Set(value string) error {
*l = append(*l, value)
return nil
}

func init() {
flag.Var(&allowedHosts, "allow-host", "Repeatable flag for hosts to allow for the URL parameter (e.g. \"d2dktr6aauwgqs.cloudfront.net\")")
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)")

}

func main() {
flag.Parse()

logger.Log(
"msg", "Starting.",
"version", Version,
"allowed_hosts", allowedHosts.String(),
"imaginary_backend", imaginaryURL,
)

rURL, err := url.Parse(imaginaryURL)
if err != nil {
panic(err)
}

rlBucket := ratelimit.NewBucketWithRate(bucketRate, bucketSize)

proxy := httputil.NewSingleHostReverseProxy(rURL)
proxy.Transport = &http.Transport{
DisableCompression: true,
DisableKeepAlives: false,
IdleConnTimeout: 5 * time.Minute,
MaxIdleConns: 10000,
MaxIdleConnsPerHost: 10000,
ResponseHeaderTimeout: 10 * time.Second,
}

s := &http.Server{
Addr: fmt.Sprintf(":%d", listenPort),
Handler: decorateHandler(proxy, rlBucket),
ReadHeaderTimeout: 2 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1 << 20,
}

s.ListenAndServe()
}

type httpHandler func(h http.Handler) http.Handler

func decorateHandler(h http.Handler, b *ratelimit.Bucket) http.Handler {
decorators := []httpHandler{
handlers.NewRateLimitHandler(b, logger),
handlers.NewIgnoreFaviconRequests(),
handlers.NewValidateURLParameter(logger, allowedHosts),
}
var handler http.Handler = h
for _, d := range decorators {
handler = d(handler)
}

return handler
}

0 comments on commit 56bb1d2

Please sign in to comment.