mirror of https://github.com/v2ray/v2ray-core
commit
e483075941
|
@ -79,66 +79,28 @@ func parseHost(rawHost string, defaultPort v2net.Port) (v2net.Destination, error
|
||||||
func (this *HttpProxyServer) handleConnection(conn *net.TCPConn) {
|
func (this *HttpProxyServer) handleConnection(conn *net.TCPConn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
reader := bufio.NewReader(conn)
|
reader := bufio.NewReader(conn)
|
||||||
for true {
|
|
||||||
request, err := http.ReadRequest(reader)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
log.Info("Request to Method [%s] Host [%s] with URL [%s]", request.Method, request.Host, request.URL.String())
|
|
||||||
defaultPort := v2net.Port(80)
|
|
||||||
if strings.ToLower(request.URL.Scheme) == "https" {
|
|
||||||
defaultPort = v2net.Port(443)
|
|
||||||
}
|
|
||||||
host := request.Host
|
|
||||||
if len(host) == 0 {
|
|
||||||
host = request.URL.Host
|
|
||||||
}
|
|
||||||
dest, err := parseHost(host, defaultPort)
|
|
||||||
if err != nil {
|
|
||||||
log.Warning("Malformed proxy host (%s): %v", host, err)
|
|
||||||
}
|
|
||||||
if strings.ToUpper(request.Method) == "CONNECT" {
|
|
||||||
this.handleConnect(request, dest, reader, conn)
|
|
||||||
} else if len(request.URL.Host) > 0 {
|
|
||||||
request.Host = request.URL.Host
|
|
||||||
request.Header.Set("Connection", "keep-alive")
|
|
||||||
request.Header.Del("Proxy-Connection")
|
|
||||||
buffer := alloc.NewBuffer().Clear()
|
|
||||||
request.Write(buffer)
|
|
||||||
log.Info("Request to remote: %s", string(buffer.Value))
|
|
||||||
packet := v2net.NewPacket(dest, buffer, true)
|
|
||||||
ray := this.space.PacketDispatcher().DispatchToOutbound(packet)
|
|
||||||
go func() {
|
|
||||||
defer close(ray.InboundInput())
|
|
||||||
responseReader := bufio.NewReader(NewChanReader(ray.InboundOutput()))
|
|
||||||
response, err := http.ReadResponse(responseReader, request)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
responseBuffer := alloc.NewBuffer().Clear()
|
request, err := http.ReadRequest(reader)
|
||||||
defer responseBuffer.Release()
|
if err != nil {
|
||||||
response.Write(responseBuffer)
|
return
|
||||||
conn.Write(responseBuffer.Value)
|
}
|
||||||
}()
|
log.Info("Request to Method [%s] Host [%s] with URL [%s]", request.Method, request.Host, request.URL.String())
|
||||||
} else {
|
defaultPort := v2net.Port(80)
|
||||||
response := &http.Response{
|
if strings.ToLower(request.URL.Scheme) == "https" {
|
||||||
Status: "400 Bad Request",
|
defaultPort = v2net.Port(443)
|
||||||
StatusCode: 400,
|
}
|
||||||
Proto: "HTTP/1.1",
|
host := request.Host
|
||||||
ProtoMajor: 1,
|
if len(host) == 0 {
|
||||||
ProtoMinor: 1,
|
host = request.URL.Host
|
||||||
Header: http.Header(make(map[string][]string)),
|
}
|
||||||
Body: nil,
|
dest, err := parseHost(host, defaultPort)
|
||||||
ContentLength: 0,
|
if err != nil {
|
||||||
Close: false,
|
log.Warning("Malformed proxy host (%s): %v", host, err)
|
||||||
}
|
}
|
||||||
|
if strings.ToUpper(request.Method) == "CONNECT" {
|
||||||
buffer := alloc.NewSmallBuffer().Clear()
|
this.handleConnect(request, dest, reader, conn)
|
||||||
response.Write(buffer)
|
} else {
|
||||||
conn.Write(buffer.Value)
|
this.handlePlainHTTP(request, dest, reader, conn)
|
||||||
buffer.Release()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,18 +128,95 @@ func (this *HttpProxyServer) handleConnect(request *http.Request, destination v2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HttpProxyServer) transport(input io.Reader, output io.Writer, ray ray.InboundRay) {
|
func (this *HttpProxyServer) transport(input io.Reader, output io.Writer, ray ray.InboundRay) {
|
||||||
var outputFinish sync.Mutex
|
var wg sync.WaitGroup
|
||||||
outputFinish.Lock()
|
wg.Add(2)
|
||||||
|
defer wg.Wait()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
v2net.ReaderToChan(ray.InboundInput(), input)
|
v2net.ReaderToChan(ray.InboundInput(), input)
|
||||||
close(ray.InboundInput())
|
close(ray.InboundInput())
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
v2net.ChanToWriter(output, ray.InboundOutput())
|
v2net.ChanToWriter(output, ray.InboundOutput())
|
||||||
outputFinish.Unlock()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
}
|
||||||
outputFinish.Lock()
|
|
||||||
|
func stripHopByHopHeaders(request *http.Request) {
|
||||||
|
// Strip hop-by-hop header basaed on RFC:
|
||||||
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
|
||||||
|
// https://www.mnot.net/blog/2011/07/11/what_proxies_must_do
|
||||||
|
|
||||||
|
request.Header.Del("Proxy-Connection")
|
||||||
|
request.Header.Del("Proxy-Authenticate")
|
||||||
|
request.Header.Del("Proxy-Authorization")
|
||||||
|
request.Header.Del("TE")
|
||||||
|
request.Header.Del("Trailers")
|
||||||
|
request.Header.Del("Transfer-Encoding")
|
||||||
|
request.Header.Del("Upgrade")
|
||||||
|
|
||||||
|
// TODO: support keep-alive
|
||||||
|
connections := request.Header.Get("Connection")
|
||||||
|
request.Header.Set("Connection", "close")
|
||||||
|
if len(connections) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, h := range strings.Split(connections, ",") {
|
||||||
|
request.Header.Del(strings.TrimSpace(h))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HttpProxyServer) handlePlainHTTP(request *http.Request, dest v2net.Destination, reader *bufio.Reader, writer io.Writer) {
|
||||||
|
if len(request.URL.Host) <= 0 {
|
||||||
|
hdr := http.Header(make(map[string][]string))
|
||||||
|
hdr.Set("Connection", "close")
|
||||||
|
response := &http.Response{
|
||||||
|
Status: "400 Bad Request",
|
||||||
|
StatusCode: 400,
|
||||||
|
Proto: "HTTP/1.1",
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Header: hdr,
|
||||||
|
Body: nil,
|
||||||
|
ContentLength: 0,
|
||||||
|
Close: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := alloc.NewSmallBuffer().Clear()
|
||||||
|
response.Write(buffer)
|
||||||
|
writer.Write(buffer.Value)
|
||||||
|
buffer.Release()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Host = request.URL.Host
|
||||||
|
stripHopByHopHeaders(request)
|
||||||
|
|
||||||
|
requestBuffer := alloc.NewBuffer().Clear()
|
||||||
|
request.Write(requestBuffer)
|
||||||
|
log.Info("Request to remote:\n%s", string(requestBuffer.Value))
|
||||||
|
|
||||||
|
packet := v2net.NewPacket(dest, requestBuffer, true)
|
||||||
|
ray := this.space.PacketDispatcher().DispatchToOutbound(packet)
|
||||||
|
defer close(ray.InboundInput())
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
responseReader := bufio.NewReader(NewChanReader(ray.InboundOutput()))
|
||||||
|
responseBuffer := alloc.NewBuffer()
|
||||||
|
defer responseBuffer.Release()
|
||||||
|
response, err := http.ReadResponse(responseReader, request)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responseBuffer.Clear()
|
||||||
|
response.Write(responseBuffer)
|
||||||
|
writer.Write(responseBuffer.Value)
|
||||||
|
response.Body.Close()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"github.com/v2ray/v2ray-core/testing/assert"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHopByHopHeadersStrip(t *testing.T) {
|
||||||
|
var rawRequest = `GET /pkg/net/http/ HTTP/1.1
|
||||||
|
Host: golang.org
|
||||||
|
Connection: keep-alive,Foo, Bar
|
||||||
|
Foo: foo
|
||||||
|
Bar: bar
|
||||||
|
Proxy-Connection: keep-alive
|
||||||
|
Proxy-Authenticate: abc
|
||||||
|
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; de-de) AppleWebKit/523.10.3 (KHTML, like Gecko) Version/3.0.4 Safari/523.10
|
||||||
|
Accept-Encoding: gzip
|
||||||
|
Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7
|
||||||
|
Cache-Control: no-cache
|
||||||
|
Accept-Language: de,en;q=0.7,en-us;q=0.3
|
||||||
|
|
||||||
|
`
|
||||||
|
b := bufio.NewReader(strings.NewReader(rawRequest))
|
||||||
|
req, err := http.ReadRequest(b)
|
||||||
|
assert.Error(err).IsNil()
|
||||||
|
assert.StringLiteral(req.Header.Get("Foo")).Equals("foo")
|
||||||
|
assert.StringLiteral(req.Header.Get("Bar")).Equals("bar")
|
||||||
|
assert.StringLiteral(req.Header.Get("Connection")).Equals("keep-alive,Foo, Bar")
|
||||||
|
assert.StringLiteral(req.Header.Get("Proxy-Connection")).Equals("keep-alive")
|
||||||
|
assert.StringLiteral(req.Header.Get("Proxy-Authenticate")).Equals("abc")
|
||||||
|
|
||||||
|
stripHopByHopHeaders(req)
|
||||||
|
assert.StringLiteral(req.Header.Get("Connection")).Equals("close")
|
||||||
|
assert.StringLiteral(req.Header.Get("Foo")).Equals("")
|
||||||
|
assert.StringLiteral(req.Header.Get("Bar")).Equals("")
|
||||||
|
assert.StringLiteral(req.Header.Get("Proxy-Connection")).Equals("")
|
||||||
|
assert.StringLiteral(req.Header.Get("Proxy-Authenticate")).Equals("")
|
||||||
|
}
|
Loading…
Reference in New Issue