package main
import (
"crypto/tls"
"flag"
"fmt"
"keybase/sketchground/aproxygo/aproxy"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"sync"
"golang.org/x/crypto/acme/autocert"
)
var (
proxies map[string]*httputil.ReverseProxy
statics map[string]http.Handler
)
// Config represents a configuration
type Config struct {
Host string
Server string
}
func main() {
cfgs := []Config{
Config{
Host: "sketchground.dk",
Server: "http://127.0.0.1:9901",
},
Config{
Host: "www.sketchground.dk",
Server: "http://127.0.0.1:9901",
},
Config{
Host: "blog.sketchground.dk",
Server: "http://127.0.0.1:9900",
},
Config{
Host: "pomodoro.sketchground.dk",
Server: "static:///var/www/pomodoro.sketchground.dk",
},
Config{
Host: "journal.sketchground.dk",
Server: "http://127.0.0.1:9900",
},
Config{
Host: "rutebil.dk",
Server: "static:///var/www/rutebildk",
},
Config{
Host: "ikurven.dk",
Server: "static:///var/www/ikurvendk",
},
Config{
Host: "www.rutebil.dk",
Server: "static:///var/www/rutebildk",
},
Config{
Host: "www.ikurven.dk",
Server: "static:///var/www/ikurvendk",
},
}
flag.Parse()
if len(flag.Args()) > 0 {
cfgs = []Config{}
}
for _, a := range flag.Args() {
arg := strings.Split(a, "=")
if len(arg) != 2 {
return
}
if arg[0] != "host" {
continue
}
data := strings.Split(arg[1], ";")
if len(data) != 2 {
continue
}
host := data[0]
server := data[1]
cfgs = append(cfgs, Config{Host: host, Server: server})
}
hosts := []string{}
// Load services...
proxies = map[string]*httputil.ReverseProxy{}
statics = map[string]http.Handler{}
for _, cfg := range cfgs {
u, _ := url.Parse(cfg.Server)
if u.Scheme == "static" {
log.Printf("Initializing static server for %v -> %v\n", cfg.Host, cfg.Server)
statics[cfg.Host] = http.FileServer(http.Dir(u.Path))
} else {
log.Printf("Initializing proxy connection for %v -> %v\n", cfg.Host, cfg.Server)
proxies[cfg.Host] = httputil.NewSingleHostReverseProxy(u)
}
hosts = append(hosts, cfg.Host)
}
mgr := &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(hosts...),
}
tracker := aproxy.NewTracker()
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
log.Println("Starting reverse proxy for ssl connections")
s := &http.Server{
Addr: ":https",
TLSConfig: &tls.Config{GetCertificate: mgr.GetCertificate},
Handler: NewP(true, tracker),
}
log.Fatal(s.ListenAndServeTLS("", ""))
wg.Done()
}()
wg.Add(1)
go func() {
log.Println("Starting reverse proxy for http connections")
log.Fatal(http.ListenAndServe(":8080", mgr.HTTPHandler(NewP(false, tracker)))) // port 80
wg.Done()
}()
wg.Wait()
tracker.Shutdown()
}
func NewP(secure bool, t *aproxy.Tracker) *P {
p := &P{secure: secure, t: t}
return p
}
// P struct
type P struct {
secure bool
t *aproxy.Tracker
}
func (p *P) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
log.Printf("Receiving quest on path: %v", req.URL)
if !p.secure { // Redirect always if not secure.
u := fmt.Sprintf("https://%v%v", req.Host, req.URL.Path)
http.Redirect(rw, req, u, http.StatusFound)
return
}
// TODO: Investigate if this should be guarded for concurrent access?
p.t.Track(req.Context(), req)
if h, ok := proxies[req.Host]; ok { // Check if we have proxies
h.ServeHTTP(rw, req)
return
}
if h, ok := statics[req.Host]; ok { // Check if we have statics
h.ServeHTTP(rw, req)
return
}
fmt.Fprintf(rw, "Nothing here. Go elsewhere.") // Return if no hosts match
return
}