github.com/chwjbn/xclash@v0.2.0/transport/simple-obfs/http.go (about) 1 package obfs 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "fmt" 7 "io" 8 "math/rand" 9 "net" 10 "net/http" 11 12 "github.com/chwjbn/xclash/common/pool" 13 ) 14 15 // HTTPObfs is shadowsocks http simple-obfs implementation 16 type HTTPObfs struct { 17 net.Conn 18 host string 19 port string 20 buf []byte 21 offset int 22 firstRequest bool 23 firstResponse bool 24 } 25 26 func (ho *HTTPObfs) Read(b []byte) (int, error) { 27 if ho.buf != nil { 28 n := copy(b, ho.buf[ho.offset:]) 29 ho.offset += n 30 if ho.offset == len(ho.buf) { 31 pool.Put(ho.buf) 32 ho.buf = nil 33 } 34 return n, nil 35 } 36 37 if ho.firstResponse { 38 buf := pool.Get(pool.RelayBufferSize) 39 n, err := ho.Conn.Read(buf) 40 if err != nil { 41 pool.Put(buf) 42 return 0, err 43 } 44 idx := bytes.Index(buf[:n], []byte("\r\n\r\n")) 45 if idx == -1 { 46 pool.Put(buf) 47 return 0, io.EOF 48 } 49 ho.firstResponse = false 50 length := n - (idx + 4) 51 n = copy(b, buf[idx+4:n]) 52 if length > n { 53 ho.buf = buf[:idx+4+length] 54 ho.offset = idx + 4 + n 55 } else { 56 pool.Put(buf) 57 } 58 return n, nil 59 } 60 return ho.Conn.Read(b) 61 } 62 63 func (ho *HTTPObfs) Write(b []byte) (int, error) { 64 if ho.firstRequest { 65 randBytes := make([]byte, 16) 66 rand.Read(randBytes) 67 req, _ := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) 68 req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%54, rand.Int()%2)) 69 req.Header.Set("Upgrade", "websocket") 70 req.Header.Set("Connection", "Upgrade") 71 req.Host = ho.host 72 if ho.port != "80" { 73 req.Host = fmt.Sprintf("%s:%s", ho.host, ho.port) 74 } 75 req.Header.Set("Sec-WebSocket-Key", base64.URLEncoding.EncodeToString(randBytes)) 76 req.ContentLength = int64(len(b)) 77 err := req.Write(ho.Conn) 78 ho.firstRequest = false 79 return len(b), err 80 } 81 82 return ho.Conn.Write(b) 83 } 84 85 // NewHTTPObfs return a HTTPObfs 86 func NewHTTPObfs(conn net.Conn, host string, port string) net.Conn { 87 return &HTTPObfs{ 88 Conn: conn, 89 firstRequest: true, 90 firstResponse: true, 91 host: host, 92 port: port, 93 } 94 }