CreateTCPStream creates a TCP stream to the given address using the client. This function should ONLY be called after successfully calling ConnectToProxy. If it succeeds, CreateTCPStream returns a Conn struct that provides a pair of I/O interfaces. Users can use the provided I/O interfaces to commu
(addr string)
| 227 | // |
| 228 | // Otherwise, CreateTCPStream returns nil and indicates the error. |
| 229 | func (c *Client) CreateTCPStream(addr string) (*Conn, error) { |
| 230 | if c.h2Transport == nil { |
| 231 | return nil, errors.New("HTTP2 connection has not been established") |
| 232 | } |
| 233 | |
| 234 | // Check |addr| is a valid URL or IPPort |
| 235 | var dst, authority string |
| 236 | ipPort, err := netaddr.ParseIPPort(addr) |
| 237 | if err != nil { |
| 238 | if addr[0] >= '0' && addr[0] <= '9' { |
| 239 | // Golang mis-parses FQDNs with leading digits, so treat it like an IP. |
| 240 | dst = "http://" + addr |
| 241 | authority = addr |
| 242 | } else { |
| 243 | parsedURL, err := url.Parse(addr) |
| 244 | if err != nil { |
| 245 | if addr[0] >= '0' && addr[0] <= '9' && strings.Contains(addr, ":") { |
| 246 | // Golang mis-parses FQDNs with leading digits, so check for basic format and pass. |
| 247 | dst = addr |
| 248 | } else { |
| 249 | return nil, errors.New("an invalid destination addr: not a IPPort or URL") |
| 250 | } |
| 251 | } else { |
| 252 | dst = parsedURL.String() |
| 253 | } |
| 254 | authority = dst |
| 255 | } |
| 256 | } else { |
| 257 | dst = fmt.Sprintf("http://%s", ipPort.String()) |
| 258 | authority = addr |
| 259 | } |
| 260 | |
| 261 | // Craft a HTTP CONNECT request. |
| 262 | pr, pw := io.Pipe() |
| 263 | req, err := http.NewRequest("CONNECT", dst, pr) |
| 264 | if err != nil { |
| 265 | return nil, err |
| 266 | } |
| 267 | req.Host = authority |
| 268 | |
| 269 | if c.authToken != "" { |
| 270 | req.Header.Set("Proxy-Authorization", fmt.Sprintf("PrivacyToken token=%s", c.authToken)) |
| 271 | } else { |
| 272 | return nil, errors.New("No proxy authorization token supplied, can't connect without one.") |
| 273 | } |
| 274 | |
| 275 | for i := 0; i < MAX_RETRIES; i += 1 { |
| 276 | // Find a gohttp2.ClientConn that can take the request. |
| 277 | _, lowlatency := c.lowLatencyAddrs[addr] |
| 278 | h2Conn, err := c.getReadyH2ClientConnOpt(addr, lowlatency) |
| 279 | if err != nil { |
| 280 | return nil, err |
| 281 | } |
| 282 | |
| 283 | // Send the request and get response. |
| 284 | req.URL.Host = c.proxyAddr |
| 285 | resp, err := h2Conn.RoundTrip(req) |
| 286 | if err != nil { |