aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2016-04-09 16:44:42 +0200
committerDimitri Sokolyuk <demon@dim13.org>2016-04-09 16:44:42 +0200
commitac2b3c495184fc4eca6823c83bbef27551eafe99 (patch)
tree8d7b5fefb21f13571b57ddcc361735a1fc8eea41
parent8548c9747fc0a4deb470fd89a255041eda2b79f1 (diff)
parenta09a47d1f8812e837080d5af9461113109555b23 (diff)
Merge branch 'master' of dim13.org:goxy
-rw-r--r--cert.go46
-rw-r--r--route.go18
-rw-r--r--rpc.go12
-rw-r--r--server.go46
-rw-r--r--ws.go41
5 files changed, 87 insertions, 76 deletions
diff --git a/cert.go b/cert.go
new file mode 100644
index 0000000..38c534d
--- /dev/null
+++ b/cert.go
@@ -0,0 +1,46 @@
+package goxy
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net/http"
+)
+
+// Certs holds certificates
+type Certs map[string]*tls.Certificate
+
+// GetCertificate returns certificate for SNI negotiation
+func (c Certs) getCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) {
+ if v, ok := c[h.ServerName]; ok {
+ return v, nil
+ }
+ return nil, fmt.Errorf("no cert for %q", h.ServerName)
+}
+
+func (c Certs) addCertificate(cert, key []byte) error {
+ crt, err := tls.X509KeyPair(cert, key)
+ if err != nil {
+ return err
+ }
+ crt.Leaf, err = x509.ParseCertificate(crt.Certificate[0])
+ if err != nil {
+ return err
+ }
+ if cn := crt.Leaf.Subject.CommonName; cn != "" {
+ c[cn] = &crt
+ }
+ for _, name := range crt.Leaf.DNSNames {
+ c[name] = &crt
+ }
+ for _, ip := range crt.Leaf.IPAddresses {
+ c[ip.String()] = &crt
+ }
+ return nil
+}
+
+func (c Certs) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
+ for k, v := range c {
+ fmt.Fprintf(w, "%v: valid untill %v\n", k, v.Leaf.NotAfter)
+ }
+}
diff --git a/route.go b/route.go
index e4de1de..26754ac 100644
--- a/route.go
+++ b/route.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"net/http"
- "net/url"
"os"
)
@@ -20,12 +19,6 @@ func (r Route) String() string {
return fmt.Sprintf("%v → %v", r.Host, r.Upstream)
}
-func (r Routes) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
- for _, v := range r {
- fmt.Fprintln(w, v)
- }
-}
-
func (r Routes) Save(fname string) error {
fd, err := os.Create(fname)
if err != nil {
@@ -44,13 +37,8 @@ func (r *Routes) Load(fname string) error {
return json.NewDecoder(fd).Decode(r)
}
-func Slug(host string) (string, bool, error) {
- h, err := url.Parse(host)
- if err != nil {
- return "", false, err
- }
- if h.Path == "" {
- h.Path = "/"
+func (r Routes) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
+ for _, v := range r {
+ fmt.Fprintln(w, v)
}
- return h.Host + h.Path, h.Scheme == "https", nil
}
diff --git a/rpc.go b/rpc.go
index 1f3c877..10c311f 100644
--- a/rpc.go
+++ b/rpc.go
@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"net/rpc"
+ "net/url"
)
var (
@@ -65,3 +66,14 @@ func (s GoXY) List(_ struct{}, ret *[]string) error {
}
return nil
}
+
+func Slug(host string) (string, bool, error) {
+ h, err := url.Parse(host)
+ if err != nil {
+ return "", false, err
+ }
+ if h.Path == "" {
+ h.Path = "/"
+ }
+ return h.Host + h.Path, h.Scheme == "https", nil
+}
diff --git a/server.go b/server.go
index a11da91..223078c 100644
--- a/server.go
+++ b/server.go
@@ -2,8 +2,7 @@ package goxy
import (
"crypto/tls"
- "crypto/x509"
- "fmt"
+ "errors"
"net/http"
"net/http/httputil"
"net/url"
@@ -12,44 +11,12 @@ import (
type Server struct {
DataFile string
Routes
- SNI
+ Certs
wwwServer http.Server
tlsServer http.Server
rpcServer http.Server
}
-// SNI holds certificates
-type SNI map[string]*tls.Certificate
-
-// GetCertificate returns certificate for SNI negotiation
-func (s SNI) getCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) {
- if v, ok := s[h.ServerName]; ok {
- return v, nil
- }
- return nil, fmt.Errorf("no cert for %q", h.ServerName)
-}
-
-func (s SNI) addCertificate(cert, key []byte) error {
- c, err := tls.X509KeyPair(cert, key)
- if err != nil {
- return err
- }
- c.Leaf, err = x509.ParseCertificate(c.Certificate[0])
- if err != nil {
- return err
- }
- if cn := c.Leaf.Subject.CommonName; cn != "" {
- s[cn] = &c
- }
- for _, name := range c.Leaf.DNSNames {
- s[name] = &c
- }
- for _, ip := range c.Leaf.IPAddresses {
- s[ip.String()] = &c
- }
- return nil
-}
-
func NewServer(dataFile, listenWWW, listenTLS, listenRPC string) (*Server, error) {
if listenRPC == "" {
listenRPC = RPCPort
@@ -57,7 +24,7 @@ func NewServer(dataFile, listenWWW, listenTLS, listenRPC string) (*Server, error
server := &Server{
DataFile: dataFile,
Routes: make(Routes),
- SNI: make(SNI),
+ Certs: make(Certs),
wwwServer: http.Server{Addr: listenWWW},
tlsServer: http.Server{Addr: listenTLS},
rpcServer: http.Server{Addr: listenRPC},
@@ -69,7 +36,8 @@ func NewServer(dataFile, listenWWW, listenTLS, listenRPC string) (*Server, error
server.Load(dataFile)
}
registerRPC(server)
- http.Handle("/debug/route", server)
+ http.Handle("/debug/routes", server.Routes)
+ http.Handle("/debug/certs", server.Certs)
return server, server.UpdateMux()
}
@@ -98,7 +66,7 @@ func (s *Server) UpdateMux() error {
case "http", "":
wwwMux.Handle(host, NewReverseProxy(upstream))
case "https":
- err := s.SNI.addCertificate(route.Cert, route.Key)
+ err := s.Certs.addCertificate(route.Cert, route.Key)
if err != nil {
return err
}
@@ -107,7 +75,7 @@ func (s *Server) UpdateMux() error {
case "ws":
wwwMux.Handle(host, NewWebSocketProxy(upstream))
case "wss":
- return fmt.Errorf("wss won't work with http/2.0")
+ return errors.New("wss won't work with http/2.0")
}
}
s.wwwServer.Handler = wwwMux
diff --git a/ws.go b/ws.go
index b54c2ce..b07a17b 100644
--- a/ws.go
+++ b/ws.go
@@ -5,41 +5,47 @@ import (
"net"
"net/http"
"net/url"
- "path"
)
type WebSocketProxy struct {
Director func(*http.Request)
}
-func (p *WebSocketProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- hj, ok := w.(http.Hijacker)
- if !ok {
- http.Error(w, "Not a hijacker", http.StatusInternalServerError)
- return
+func NewWebSocketProxy(target *url.URL) *WebSocketProxy {
+ director := func(req *http.Request) {
+ req.URL.Scheme = target.Scheme
+ req.URL.Host = target.Host
}
+ return &WebSocketProxy{Director: director}
+}
- conn, _, err := hj.Hijack()
+func (p *WebSocketProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ p.Director(r)
+ d, err := net.Dial("tcp", r.URL.Host)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- defer conn.Close()
-
- p.Director(r)
+ defer d.Close()
- d, err := net.Dial("tcp", r.URL.Host)
+ // pass first request to upstream
+ err = r.Write(d)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- defer d.Close()
- err = r.Write(d)
+ hj, ok := w.(http.Hijacker)
+ if !ok {
+ http.Error(w, "Not a hijacker", http.StatusInternalServerError)
+ return
+ }
+ conn, _, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
+ defer conn.Close()
errc := make(chan error, 2)
cp := func(dst io.Writer, src io.Reader) {
@@ -50,12 +56,3 @@ func (p *WebSocketProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
go cp(conn, d)
<-errc
}
-
-func NewWebSocketProxy(target *url.URL) *WebSocketProxy {
- director := func(req *http.Request) {
- req.URL.Scheme = target.Scheme
- req.URL.Host = target.Host
- req.URL.Path = path.Join(target.Path, req.URL.Path)
- }
- return &WebSocketProxy{Director: director}
-}