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
|
package goxy
import (
"crypto/tls"
"net"
"net/http"
"net/http/httputil"
"net/url"
)
type Server struct {
DataFile string
Routes
SNI
wwwServer http.Server
tlsServer http.Server
rpcServer http.Server
}
func NewServer(dataFile, listenWWW, listenTLS, listenRPC string) (*Server, error) {
if listenRPC == "" {
listenRPC = RPCPort
}
server := &Server{
DataFile: dataFile,
Routes: make(Routes),
SNI: make(SNI),
wwwServer: http.Server{Addr: listenWWW},
tlsServer: http.Server{Addr: listenTLS},
rpcServer: http.Server{Addr: listenRPC},
}
server.tlsServer.TLSConfig = &tls.Config{
GetCertificate: server.getCertificate,
}
if dataFile != "" {
server.Load(dataFile)
}
RegisterRPC(server)
http.Handle("/debug/route", server)
return server, server.UpdateMux()
}
func NewRedirect(host string) http.Handler {
return http.RedirectHandler(host, http.StatusMovedPermanently)
}
func NewReverseProxy(target *url.URL) *httputil.ReverseProxy {
return httputil.NewSingleHostReverseProxy(target)
}
// Update routes from in-memory state
func (s *Server) UpdateMux() error {
wwwMux := http.NewServeMux()
tlsMux := http.NewServeMux()
for host, r := range s.Routes {
serverName, err := url.Parse(r.Host)
if err != nil {
return err
}
upstream, err := url.Parse(r.Upstream)
if err != nil {
return err
}
if serverName.Scheme == "https" {
cert, err := tls.X509KeyPair(r.Cert, r.Key)
if err != nil {
return err
}
slug, _, err := net.SplitHostPort(serverName.Host)
if err != nil {
slug = serverName.Host
}
s.SNI[slug] = &cert
}
switch serverName.Scheme {
case "http", "":
wwwMux.Handle(host, NewReverseProxy(upstream))
case "https":
wwwMux.Handle(host, NewRedirect("https://"+host))
tlsMux.Handle(host, NewReverseProxy(upstream))
case "ws":
wwwMux.Handle(host, NewWebSocketProxy(upstream))
case "wss":
wwwMux.Handle(host, NewRedirect("wss://"+host))
tlsMux.Handle(host, NewWebSocketProxy(upstream))
}
}
s.wwwServer.Handler = wwwMux
s.tlsServer.Handler = tlsMux
return nil
}
func (s *Server) Start() error {
errc := make(chan error)
go func() { errc <- s.wwwServer.ListenAndServe() }()
go func() { errc <- s.tlsServer.ListenAndServeTLS("", "") }()
go func() { errc <- s.rpcServer.ListenAndServe() }()
return <-errc
}
|