mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-04 20:37:40 +03:00
97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
package http
|
|
|
|
import (
|
|
std_bufio "bufio"
|
|
"context"
|
|
"encoding/base64"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
|
|
"github.com/sagernet/sing/common/buf"
|
|
"github.com/sagernet/sing/common/bufio"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
M "github.com/sagernet/sing/common/metadata"
|
|
N "github.com/sagernet/sing/common/network"
|
|
)
|
|
|
|
var _ N.Dialer = (*Client)(nil)
|
|
|
|
type Client struct {
|
|
dialer N.Dialer
|
|
serverAddr M.Socksaddr
|
|
username string
|
|
password string
|
|
}
|
|
|
|
func NewClient(dialer N.Dialer, serverAddr M.Socksaddr, username string, password string) *Client {
|
|
return &Client{
|
|
dialer,
|
|
serverAddr,
|
|
username,
|
|
password,
|
|
}
|
|
}
|
|
|
|
func (c *Client) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
network = N.NetworkName(network)
|
|
switch network {
|
|
case N.NetworkTCP:
|
|
case N.NetworkUDP:
|
|
return nil, os.ErrInvalid
|
|
default:
|
|
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
|
}
|
|
var conn net.Conn
|
|
conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.serverAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
destinationAddress := destination.String()
|
|
request := &http.Request{
|
|
Method: http.MethodConnect,
|
|
URL: &url.URL{
|
|
Host: destinationAddress,
|
|
},
|
|
Host: destinationAddress,
|
|
Header: http.Header{
|
|
"Proxy-Connection": []string{"Keep-Alive"},
|
|
},
|
|
}
|
|
if c.username != "" {
|
|
auth := c.username + ":" + c.password
|
|
request.Header.Add("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
|
|
}
|
|
err = request.Write(conn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reader := std_bufio.NewReader(conn)
|
|
response, err := http.ReadResponse(reader, request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch response.StatusCode {
|
|
case http.StatusOK:
|
|
if reader.Buffered() > 0 {
|
|
buffer := buf.NewSize(reader.Buffered())
|
|
_, err = buffer.ReadFullFrom(reader, buffer.FreeLen())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn = bufio.NewCachedConn(conn, buffer)
|
|
}
|
|
return conn, nil
|
|
case http.StatusProxyAuthRequired:
|
|
return nil, E.New("authentication required")
|
|
case http.StatusMethodNotAllowed:
|
|
return nil, E.New("method not allowed")
|
|
default:
|
|
return nil, E.New("unexpected status: ", response.Status)
|
|
}
|
|
}
|
|
|
|
func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
|
return nil, os.ErrInvalid
|
|
}
|