mirror of
https://github.com/bingohuang/docker-labs.git
synced 2025-07-13 17:42:53 +08:00
153 lines
3.5 KiB
Go
153 lines
3.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/play-with-docker/play-with-docker/config"
|
|
)
|
|
|
|
func getTargetInfo(vars map[string]string, req *http.Request) (string, string) {
|
|
node := vars["node"]
|
|
port := vars["port"]
|
|
hostPort := strings.Split(req.Host, ":")
|
|
|
|
// give priority to the URL host port
|
|
if len(hostPort) > 1 && hostPort[1] != config.PortNumber {
|
|
port = hostPort[1]
|
|
} else if port == "" {
|
|
port = "80"
|
|
}
|
|
|
|
if strings.HasPrefix(node, "pwd") {
|
|
// Node is actually an ip, need to convert underscores by dots.
|
|
ip := strings.Replace(strings.TrimPrefix(node, "pwd"), "_", ".", -1)
|
|
|
|
if net.ParseIP(ip) == nil {
|
|
// Not a valid IP, so treat this is a hostname.
|
|
} else {
|
|
node = ip
|
|
}
|
|
}
|
|
|
|
return node, port
|
|
|
|
}
|
|
|
|
type tcpProxy struct {
|
|
Director func(*http.Request)
|
|
ErrorLog *log.Logger
|
|
Dial func(network, addr string) (net.Conn, error)
|
|
}
|
|
|
|
func (p *tcpProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
logFunc := log.Printf
|
|
if p.ErrorLog != nil {
|
|
logFunc = p.ErrorLog.Printf
|
|
}
|
|
|
|
outreq := new(http.Request)
|
|
// shallow copying
|
|
*outreq = *r
|
|
p.Director(outreq)
|
|
host := outreq.URL.Host
|
|
|
|
dial := p.Dial
|
|
if dial == nil {
|
|
dial = net.Dial
|
|
}
|
|
|
|
if outreq.URL.Scheme == "wss" || outreq.URL.Scheme == "https" {
|
|
var tlsConfig *tls.Config
|
|
tlsConfig = &tls.Config{InsecureSkipVerify: true}
|
|
dial = func(network, address string) (net.Conn, error) {
|
|
return tls.Dial("tcp", host, tlsConfig)
|
|
}
|
|
}
|
|
|
|
d, err := dial("tcp", host)
|
|
if err != nil {
|
|
http.Error(w, "Error forwarding request.", 500)
|
|
logFunc("Error dialing websocket backend %s: %v", outreq.URL, err)
|
|
return
|
|
}
|
|
// All request generated by the http package implement this interface.
|
|
hj, ok := w.(http.Hijacker)
|
|
if !ok {
|
|
http.Error(w, "Not a hijacker?", 500)
|
|
return
|
|
}
|
|
// Hijack() tells the http package not to do anything else with the connection.
|
|
// After, it bcomes this functions job to manage it. `nc` is of type *net.Conn.
|
|
nc, _, err := hj.Hijack()
|
|
if err != nil {
|
|
logFunc("Hijack error: %v", err)
|
|
return
|
|
}
|
|
defer nc.Close() // must close the underlying net connection after hijacking
|
|
defer d.Close()
|
|
|
|
// write the modified incoming request to the dialed connection
|
|
err = outreq.Write(d)
|
|
if err != nil {
|
|
logFunc("Error copying request to target: %v", err)
|
|
return
|
|
}
|
|
errc := make(chan error, 2)
|
|
cp := func(dst io.Writer, src io.Reader) {
|
|
_, err := io.Copy(dst, src)
|
|
errc <- err
|
|
}
|
|
go cp(d, nc)
|
|
go cp(nc, d)
|
|
<-errc
|
|
}
|
|
func NewTCPProxy() http.Handler {
|
|
director := func(req *http.Request) {
|
|
v := mux.Vars(req)
|
|
|
|
node, port := getTargetInfo(v, req)
|
|
|
|
if port == "443" {
|
|
if strings.Contains(req.URL.Scheme, "http") {
|
|
req.URL.Scheme = "https"
|
|
} else {
|
|
req.URL.Scheme = "wss"
|
|
}
|
|
}
|
|
req.URL.Host = fmt.Sprintf("%s:%s", node, port)
|
|
}
|
|
return &tcpProxy{Director: director}
|
|
}
|
|
|
|
func NewSSLDaemonHandler() http.Handler {
|
|
director := func(req *http.Request) {
|
|
v := mux.Vars(req)
|
|
node := v["node"]
|
|
if strings.HasPrefix(node, "pwd") {
|
|
// Node is actually an ip, need to convert underscores by dots.
|
|
ip := strings.Replace(strings.TrimPrefix(node, "pwd"), "_", ".", -1)
|
|
|
|
if net.ParseIP(ip) == nil {
|
|
// Not a valid IP, so treat this is a hostname.
|
|
} else {
|
|
node = ip
|
|
}
|
|
}
|
|
|
|
// Only proxy http for now
|
|
req.URL.Scheme = "http"
|
|
|
|
req.URL.Host = fmt.Sprintf("%s:%s", node, "2375")
|
|
log.Printf("HTTPS Reverse proxying to %s\n", req.URL.Host)
|
|
}
|
|
|
|
return &tcpProxy{Director: director}
|
|
}
|