|
|
@@ -1,6 +1,7 @@
|
|
|
package v2rayhttp
|
|
|
|
|
|
import (
|
|
|
+ "bufio"
|
|
|
"context"
|
|
|
"crypto/tls"
|
|
|
"io"
|
|
|
@@ -20,20 +21,25 @@ import (
|
|
|
var _ adapter.V2RayClientTransport = (*Client)(nil)
|
|
|
|
|
|
type Client struct {
|
|
|
- ctx context.Context
|
|
|
- client *http.Client
|
|
|
- url *url.URL
|
|
|
- host []string
|
|
|
- method string
|
|
|
- headers http.Header
|
|
|
+ ctx context.Context
|
|
|
+ dialer N.Dialer
|
|
|
+ serverAddr M.Socksaddr
|
|
|
+ client *http.Client
|
|
|
+ http2 bool
|
|
|
+ url *url.URL
|
|
|
+ host []string
|
|
|
+ method string
|
|
|
+ headers http.Header
|
|
|
}
|
|
|
|
|
|
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
|
|
|
client := &Client{
|
|
|
- ctx: ctx,
|
|
|
- host: options.Host,
|
|
|
- method: options.Method,
|
|
|
- headers: make(http.Header),
|
|
|
+ ctx: ctx,
|
|
|
+ dialer: dialer,
|
|
|
+ serverAddr: serverAddr,
|
|
|
+ host: options.Host,
|
|
|
+ method: options.Method,
|
|
|
+ headers: make(http.Header),
|
|
|
client: &http.Client{
|
|
|
Transport: &http.Transport{
|
|
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
@@ -43,6 +49,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|
|
TLSClientConfig: tlsConfig,
|
|
|
},
|
|
|
},
|
|
|
+ http2: tlsConfig != nil,
|
|
|
}
|
|
|
if client.method == "" {
|
|
|
client.method = "PUT"
|
|
|
@@ -66,13 +73,54 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|
|
}
|
|
|
|
|
|
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
|
|
+ if !c.http2 {
|
|
|
+ return c.dialHTTP()
|
|
|
+ } else {
|
|
|
+ return c.dialHTTP2()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (c *Client) dialHTTP() (net.Conn, error) {
|
|
|
+ conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, c.serverAddr)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ request := &http.Request{
|
|
|
+ Method: c.method,
|
|
|
+ URL: c.url,
|
|
|
+ ProtoMajor: 1,
|
|
|
+ Proto: "HTTP/1.1",
|
|
|
+ Header: c.headers.Clone(),
|
|
|
+ }
|
|
|
+ switch hostLen := len(c.host); hostLen {
|
|
|
+ case 0:
|
|
|
+ case 1:
|
|
|
+ request.Host = c.host[0]
|
|
|
+ default:
|
|
|
+ request.Host = c.host[rand.Intn(hostLen)]
|
|
|
+ }
|
|
|
+ err = request.Write(conn)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ reader := bufio.NewReader(conn)
|
|
|
+ response, err := http.ReadResponse(reader, request)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if response.StatusCode != 200 {
|
|
|
+ return nil, E.New("unexpected status: ", response.Status)
|
|
|
+ }
|
|
|
+ return conn, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (c *Client) dialHTTP2() (net.Conn, error) {
|
|
|
pipeInReader, pipeInWriter := io.Pipe()
|
|
|
request := &http.Request{
|
|
|
Method: c.method,
|
|
|
Body: pipeInReader,
|
|
|
URL: c.url,
|
|
|
ProtoMajor: 2,
|
|
|
- ProtoMinor: 0,
|
|
|
Proto: "HTTP/2",
|
|
|
Header: c.headers.Clone(),
|
|
|
}
|