initial commit
This commit is contained in:
233
server/handler/http/handler.go
Normal file
233
server/handler/http/handler.go
Normal file
@ -0,0 +1,233 @@
|
||||
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)
|
||||
}
|
Reference in New Issue
Block a user