Improve: using native http request

This commit is contained in:
Dreamacro 2018-06-14 01:00:58 +08:00
parent 6e2b9edb45
commit 47e038cf18
12 changed files with 169 additions and 96 deletions

View file

@ -12,13 +12,8 @@ type DirectAdapter struct {
conn net.Conn conn net.Conn
} }
// Writer is used to output network traffic // ReadWriter is used to handle network traffic
func (d *DirectAdapter) Writer() io.Writer { func (d *DirectAdapter) ReadWriter() io.ReadWriter {
return d.conn
}
// Reader is used to input network traffic
func (d *DirectAdapter) Reader() io.Reader {
return d.conn return d.conn
} }
@ -27,6 +22,11 @@ func (d *DirectAdapter) Close() {
d.conn.Close() d.conn.Close()
} }
// Close is used to close connection
func (d *DirectAdapter) Conn() net.Conn {
return d.conn
}
type Direct struct { type Direct struct {
} }

View file

@ -2,6 +2,7 @@ package adapters
import ( import (
"io" "io"
"net"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
@ -10,18 +11,17 @@ import (
type RejectAdapter struct { type RejectAdapter struct {
} }
// Writer is used to output network traffic // ReadWriter is used to handle network traffic
func (r *RejectAdapter) Writer() io.Writer { func (r *RejectAdapter) ReadWriter() io.ReadWriter {
return &NopRW{}
}
// Reader is used to input network traffic
func (r *RejectAdapter) Reader() io.Reader {
return &NopRW{} return &NopRW{}
} }
// Close is used to close connection // Close is used to close connection
func (r *RejectAdapter) Close() { func (r *RejectAdapter) Close() {}
// Close is used to close connection
func (r *RejectAdapter) Conn() net.Conn {
return nil
} }
type Reject struct { type Reject struct {

View file

@ -19,13 +19,8 @@ type ShadowsocksAdapter struct {
conn net.Conn conn net.Conn
} }
// Writer is used to output network traffic // ReadWriter is used to handle network traffic
func (ss *ShadowsocksAdapter) Writer() io.Writer { func (ss *ShadowsocksAdapter) ReadWriter() io.ReadWriter {
return ss.conn
}
// Reader is used to input network traffic
func (ss *ShadowsocksAdapter) Reader() io.Reader {
return ss.conn return ss.conn
} }
@ -34,6 +29,10 @@ func (ss *ShadowsocksAdapter) Close() {
ss.conn.Close() ss.conn.Close()
} }
func (ss *ShadowsocksAdapter) Conn() net.Conn {
return ss.conn
}
type ShadowSocks struct { type ShadowSocks struct {
server string server string
cipher string cipher string

View file

@ -2,17 +2,19 @@ package constant
import ( import (
"io" "io"
"net"
) )
type ProxyAdapter interface { type ProxyAdapter interface {
Writer() io.Writer ReadWriter() io.ReadWriter
Reader() io.Reader Conn() net.Conn
Close() Close()
} }
type ServerAdapter interface { type ServerAdapter interface {
Addr() *Addr Addr() *Addr
ProxyAdapter Connect(ProxyAdapter)
Close()
} }
type Proxy interface { type Proxy interface {

View file

@ -9,10 +9,23 @@ const (
AtypIPv4 = 1 AtypIPv4 = 1
AtypDomainName = 3 AtypDomainName = 3
AtypIPv6 = 4 AtypIPv6 = 4
TCP = iota
UDP
) )
type NetWork int
func (n *NetWork) String() string {
if *n == TCP {
return "tcp"
}
return "udp"
}
// Addr is used to store connection address // Addr is used to store connection address
type Addr struct { type Addr struct {
NetWork NetWork
AddrType int AddrType int
Host string Host string
IP *net.IP IP *net.IP

View file

@ -6,7 +6,8 @@ import (
"syscall" "syscall"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/proxy" "github.com/Dreamacro/clash/proxy/http"
"github.com/Dreamacro/clash/proxy/socks"
"github.com/Dreamacro/clash/tunnel" "github.com/Dreamacro/clash/tunnel"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -33,8 +34,8 @@ func main() {
log.Fatalf("Parse config error: %s", err.Error()) log.Fatalf("Parse config error: %s", err.Error())
} }
go proxy.NewHttpProxy(port) go http.NewHttpProxy(port)
go proxy.NewSocksProxy(socksPort) go socks.NewSocksProxy(socksPort)
sigCh := make(chan os.Signal, 1) sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

62
proxy/http/http.go Normal file
View file

@ -0,0 +1,62 @@
package http
import (
"io"
"net"
"net/http"
"time"
C "github.com/Dreamacro/clash/constant"
)
type HttpAdapter struct {
addr *C.Addr
r *http.Request
w http.ResponseWriter
done chan struct{}
}
func (h *HttpAdapter) Close() {
h.done <- struct{}{}
}
func (h *HttpAdapter) Addr() *C.Addr {
return h.addr
}
func (h *HttpAdapter) Connect(proxy C.ProxyAdapter) {
req := http.Transport{
Dial: func(string, string) (net.Conn, error) {
return proxy.Conn(), nil
},
// from http.DefaultTransport
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
resp, err := req.RoundTrip(h.r)
if err != nil {
return
}
defer resp.Body.Close()
header := h.w.Header()
for k, vv := range resp.Header {
for _, v := range vv {
header.Add(k, v)
}
}
h.w.WriteHeader(resp.StatusCode)
io.Copy(h.w, resp.Body)
}
func NewHttp(host string, w http.ResponseWriter, r *http.Request) (*HttpAdapter, chan struct{}) {
done := make(chan struct{})
return &HttpAdapter{
addr: parseHttpAddr(host),
r: r,
w: w,
done: done,
}, done
}

35
proxy/http/https.go Normal file
View file

@ -0,0 +1,35 @@
package http
import (
"bufio"
"io"
"net"
C "github.com/Dreamacro/clash/constant"
)
type HttpsAdapter struct {
addr *C.Addr
conn net.Conn
rw *bufio.ReadWriter
}
func (h *HttpsAdapter) Close() {
h.conn.Close()
}
func (h *HttpsAdapter) Addr() *C.Addr {
return h.addr
}
func (h *HttpsAdapter) Connect(proxy C.ProxyAdapter) {
go io.Copy(h.conn, proxy.ReadWriter())
io.Copy(proxy.ReadWriter(), h.conn)
}
func NewHttps(host string, conn net.Conn) *HttpsAdapter {
return &HttpsAdapter{
addr: parseHttpAddr(host),
conn: conn,
}
}

View file

@ -1,21 +1,22 @@
package proxy package http
import ( import (
"bufio"
"bytes"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"net/http/httputil"
"strings" "strings"
"github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/tunnel"
"github.com/riobard/go-shadowsocks2/socks" "github.com/riobard/go-shadowsocks2/socks"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
var (
tun = tunnel.GetInstance()
)
func NewHttpProxy(port string) { func NewHttpProxy(port string) {
server := &http.Server{ server := &http.Server{
Addr: fmt.Sprintf(":%s", port), Addr: fmt.Sprintf(":%s", port),
@ -32,21 +33,14 @@ func NewHttpProxy(port string) {
} }
func handleHTTP(w http.ResponseWriter, r *http.Request) { func handleHTTP(w http.ResponseWriter, r *http.Request) {
buf, _ := httputil.DumpRequestOut(r, true)
hijacker, ok := w.(http.Hijacker)
if !ok {
return
}
conn, rw, err := hijacker.Hijack()
if err != nil {
return
}
addr := r.Host addr := r.Host
// padding default port // padding default port
if !strings.Contains(addr, ":") { if !strings.Contains(addr, ":") {
addr += ":80" addr += ":80"
} }
tun.Add(NewHttp(addr, conn, rw, buf)) req, done := NewHttp(addr, w, r)
tun.Add(req)
<-done
} }
func handleTunneling(w http.ResponseWriter, r *http.Request) { func handleTunneling(w http.ResponseWriter, r *http.Request) {
@ -54,38 +48,16 @@ func handleTunneling(w http.ResponseWriter, r *http.Request) {
if !ok { if !ok {
return return
} }
conn, rw, err := hijacker.Hijack() conn, _, err := hijacker.Hijack()
if err != nil { if err != nil {
return return
} }
// w.WriteHeader(http.StatusOK) doesn't works in Safari // w.WriteHeader(http.StatusOK) doesn't works in Safari
conn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) conn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
tun.Add(NewHttp(r.Host, conn, rw, []byte{})) tun.Add(NewHttps(r.Host, conn))
} }
type HttpAdapter struct { func parseHttpAddr(target string) *C.Addr {
addr *constant.Addr
conn net.Conn
r io.Reader
}
func (h *HttpAdapter) Writer() io.Writer {
return h.conn
}
func (h *HttpAdapter) Reader() io.Reader {
return h.r
}
func (h *HttpAdapter) Close() {
h.conn.Close()
}
func (h *HttpAdapter) Addr() *constant.Addr {
return h.addr
}
func parseHttpAddr(target string) *constant.Addr {
host, port, _ := net.SplitHostPort(target) host, port, _ := net.SplitHostPort(target)
ipAddr, _ := net.ResolveIPAddr("ip", host) ipAddr, _ := net.ResolveIPAddr("ip", host)
var addType int var addType int
@ -99,19 +71,11 @@ func parseHttpAddr(target string) *constant.Addr {
addType = socks.AtypIPv4 addType = socks.AtypIPv4
} }
return &constant.Addr{ return &C.Addr{
NetWork: C.TCP,
AddrType: addType, AddrType: addType,
Host: host, Host: host,
IP: &ipAddr.IP, IP: &ipAddr.IP,
Port: port, Port: port,
} }
} }
func NewHttp(host string, conn net.Conn, rw *bufio.ReadWriter, payload []byte) *HttpAdapter {
r := io.MultiReader(bytes.NewReader(payload), rw)
return &HttpAdapter{
conn: conn,
addr: parseHttpAddr(host),
r: r,
}
}

View file

@ -1,4 +1,4 @@
package proxy package socks
import ( import (
"fmt" "fmt"
@ -6,7 +6,7 @@ import (
"net" "net"
"strconv" "strconv"
"github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/tunnel" "github.com/Dreamacro/clash/tunnel"
"github.com/riobard/go-shadowsocks2/socks" "github.com/riobard/go-shadowsocks2/socks"
@ -45,26 +45,23 @@ func handleSocks(conn net.Conn) {
type SocksAdapter struct { type SocksAdapter struct {
conn net.Conn conn net.Conn
addr *constant.Addr addr *C.Addr
}
func (s *SocksAdapter) Writer() io.Writer {
return s.conn
}
func (s *SocksAdapter) Reader() io.Reader {
return s.conn
} }
func (s *SocksAdapter) Close() { func (s *SocksAdapter) Close() {
s.conn.Close() s.conn.Close()
} }
func (s *SocksAdapter) Addr() *constant.Addr { func (s *SocksAdapter) Addr() *C.Addr {
return s.addr return s.addr
} }
func parseSocksAddr(target socks.Addr) *constant.Addr { func (s *SocksAdapter) Connect(proxy C.ProxyAdapter) {
go io.Copy(s.conn, proxy.ReadWriter())
io.Copy(proxy.ReadWriter(), s.conn)
}
func parseSocksAddr(target socks.Addr) *C.Addr {
var host, port string var host, port string
var ip net.IP var ip net.IP
@ -84,7 +81,8 @@ func parseSocksAddr(target socks.Addr) *constant.Addr {
port = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1])) port = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1]))
} }
return &constant.Addr{ return &C.Addr{
NetWork: C.TCP,
AddrType: int(target[0]), AddrType: int(target[0]),
Host: host, Host: host,
IP: &ip, IP: &ip,

1
proxy/socks/udp.go Normal file
View file

@ -0,0 +1 @@
package socks

View file

@ -2,7 +2,6 @@ package tunnel
import ( import (
"fmt" "fmt"
"io"
"strings" "strings"
"sync" "sync"
@ -110,8 +109,7 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
} }
defer remoConn.Close() defer remoConn.Close()
go io.Copy(localConn.Writer(), remoConn.Reader()) localConn.Connect(remoConn)
io.Copy(remoConn.Writer(), localConn.Reader())
} }
func (t *Tunnel) match(addr *C.Addr) C.Proxy { func (t *Tunnel) match(addr *C.Addr) C.Proxy {