gost/server/handler/http/handler.go
2021-03-30 22:34:01 +08:00

234 lines
4.9 KiB
Go

package http
import (
"bufio"
"context"
"net"
"net/http"
"github.com/go-gost/gost/logger"
"github.com/go-gost/gost/server/handler"
)
var (
_ handler.Handler = (*Handler)(nil)
)
type Handler struct {
logger logger.Logger
md metadata
}
func NewHandler(opts ...handler.Option) *Handler {
options := &handler.Options{}
for _, opt := range opts {
opt(options)
}
return &Handler{
logger: options.Logger,
}
}
func (h *Handler) Init(md handler.Metadata) error {
return nil
}
func (h *Handler) Handle(ctx context.Context, conn net.Conn) {
defer conn.Close()
req, err := http.ReadRequest(bufio.NewReader(conn))
if err != nil {
h.logger.WithFields(map[string]interface{}{
"src": conn.RemoteAddr(),
"local": conn.LocalAddr(),
}).Error(err)
return
}
defer req.Body.Close()
h.handleRequest(conn, req)
}
func (h *Handler) handleRequest(conn net.Conn, req *http.Request) {
if req == nil {
return
}
/*
// try to get the actual host.
if v := req.Header.Get("Gost-Target"); v != "" {
if h, err := decodeServerName(v); err == nil {
req.Host = h
}
}
*/
host := req.Host
if _, port, _ := net.SplitHostPort(host); port == "" {
host = net.JoinHostPort(host, "80")
}
/*
u, _, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
if u != "" {
u += "@"
}
log.Logf("[http] %s%s -> %s -> %s",
u, conn.RemoteAddr(), h.options.Node.String(), host)
if Debug {
dump, _ := httputil.DumpRequest(req, false)
log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
req.Header.Del("Gost-Target")
*/
resp := &http.Response{
ProtoMajor: 1,
ProtoMinor: 1,
Header: http.Header{},
}
resp.Header.Add("Proxy-Agent", h.md.proxyAgent)
/*
if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) {
log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s",
conn.RemoteAddr(), conn.LocalAddr(), host)
resp.StatusCode = http.StatusForbidden
if Debug {
dump, _ := httputil.DumpResponse(resp, false)
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
resp.Write(conn)
return
}
*/
/*
if h.options.Bypass.Contains(host) {
resp.StatusCode = http.StatusForbidden
log.Logf("[http] %s - %s bypass %s",
conn.RemoteAddr(), conn.LocalAddr(), host)
if Debug {
dump, _ := httputil.DumpResponse(resp, false)
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
resp.Write(conn)
return
}
*/
/*
if !h.authenticate(conn, req, resp) {
return
}
*/
if req.Method == "PRI" ||
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
resp.StatusCode = http.StatusBadRequest
/*
if Debug {
dump, _ := httputil.DumpResponse(resp, false)
log.Logf("[http] %s <- %s\n%s",
conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
*/
resp.Write(conn)
return
}
req.Header.Del("Proxy-Authorization")
/*
retries := 1
if h.options.Chain != nil && h.options.Chain.Retries > 0 {
retries = h.options.Chain.Retries
}
if h.options.Retries > 0 {
retries = h.options.Retries
}
var err error
var cc net.Conn
var route *Chain
for i := 0; i < retries; i++ {
route, err = h.options.Chain.selectRouteFor(host)
if err != nil {
log.Logf("[http] %s -> %s : %s",
conn.RemoteAddr(), conn.LocalAddr(), err)
continue
}
buf := bytes.Buffer{}
fmt.Fprintf(&buf, "%s -> %s -> ",
conn.RemoteAddr(), h.options.Node.String())
for _, nd := range route.route {
fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String())
}
fmt.Fprintf(&buf, "%s", host)
log.Log("[route]", buf.String())
// forward http request
lastNode := route.LastNode()
if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
err = h.forwardRequest(conn, req, route)
if err == nil {
return
}
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
continue
}
cc, err = route.Dial(host,
TimeoutChainOption(h.options.Timeout),
HostsChainOption(h.options.Hosts),
ResolverChainOption(h.options.Resolver),
)
if err == nil {
break
}
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
}
if err != nil {
resp.StatusCode = http.StatusServiceUnavailable
if Debug {
dump, _ := httputil.DumpResponse(resp, false)
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
resp.Write(conn)
return
}
*/
cc, err := net.Dial("tcp", host)
if err != nil {
resp.StatusCode = http.StatusServiceUnavailable
resp.Write(conn)
return
}
defer cc.Close()
if req.Method == http.MethodConnect {
b := []byte("HTTP/1.1 200 Connection established\r\n" +
"Proxy-Agent: " + h.md.proxyAgent + "\r\n\r\n")
conn.Write(b)
} else {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
return
}
}
handler.Transport(conn, cc)
}