123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- package vmess
- import (
- "bufio"
- "bytes"
- "errors"
- "fmt"
- "net"
- "net/http"
- "net/textproto"
- "github.com/metacubex/mihomo/common/utils"
- "github.com/metacubex/randv2"
- )
- type httpConn struct {
- net.Conn
- cfg *HTTPConfig
- reader *bufio.Reader
- whandshake bool
- }
- type HTTPConfig struct {
- Method string
- Host string
- Path []string
- Headers map[string][]string
- }
- // Read implements net.Conn.Read()
- func (hc *httpConn) Read(b []byte) (int, error) {
- if hc.reader != nil {
- n, err := hc.reader.Read(b)
- return n, err
- }
- reader := textproto.NewConn(hc.Conn)
- // First line: GET /index.html HTTP/1.0
- if _, err := reader.ReadLine(); err != nil {
- return 0, err
- }
- if _, err := reader.ReadMIMEHeader(); err != nil {
- return 0, err
- }
- hc.reader = reader.R
- return reader.R.Read(b)
- }
- // Write implements io.Writer.
- func (hc *httpConn) Write(b []byte) (int, error) {
- if hc.whandshake {
- return hc.Conn.Write(b)
- }
- if len(hc.cfg.Path) == 0 {
- return -1, errors.New("path is empty")
- }
- path := hc.cfg.Path[randv2.IntN(len(hc.cfg.Path))]
- host := hc.cfg.Host
- if header := hc.cfg.Headers["Host"]; len(header) != 0 {
- host = header[randv2.IntN(len(header))]
- }
- u := fmt.Sprintf("http://%s%s", net.JoinHostPort(host, "80"), path)
- req, err := http.NewRequest(utils.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b))
- if err != nil {
- return 0, err
- }
- for key, list := range hc.cfg.Headers {
- req.Header.Set(key, list[randv2.IntN(len(list))])
- }
- req.ContentLength = int64(len(b))
- if err := req.Write(hc.Conn); err != nil {
- return 0, err
- }
- hc.whandshake = true
- return len(b), nil
- }
- func (hc *httpConn) Close() error {
- return hc.Conn.Close()
- }
- func StreamHTTPConn(conn net.Conn, cfg *HTTPConfig) net.Conn {
- return &httpConn{
- Conn: conn,
- cfg: cfg,
- }
- }
|