Go net/http with AeonProxy proxies

Set the proxy on an http.Transport, and every request from that client goes out through residential or mobile IPs, over HTTP or SOCKS5.

Protocols
HTTP proxy for HTTP and HTTPS sites; SOCKS5 in the standard library or with golang.org/x/net/proxy
Authentication
Login and password in the proxy URL, or proxy.Auth with x/net
Rotation
A new IP per connection (a Transport reuses its connections); a -sid- key keeps one IP for up to 60 minutes
Works with
Residential and mobile proxies

Before you start

  • A current Go release with modules. For the SOCKS5 example, go get golang.org/x/net/proxy; if the current x/net needs a newer Go than yours, the go command downloads that toolchain itself (the default GOTOOLCHAIN=auto).
  • Your proxy login and password from the Residential or Mobile page of the dashboard, and traffic on that product: without traffic the gateway answers 402.
Connection details
Host
gate.aeonproxy.com
Port (HTTP / HTTPS)
7000
Port (SOCKS5)
7001
Login
your proxy login, with targeting keys if you need them: LOGIN-cc-us-sid-abc123
Password
your proxy password

Copy the login and password from the Residential or Mobile page of the dashboard; the generator there also writes ready lines. Key syntax: targeting and sessions.

HTTP proxy with http.ProxyURL

Give a Transport Proxy: http.ProxyURL(...) with the login and password in the URL. Go sends them in a Proxy-Authorization header, and for HTTPS sites it opens a CONNECT tunnel, so the scheme stays http://.

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

func main() {
	proxyURL, err := url.Parse("http://LOGIN-cc-us:[email protected]:7000")
	if err != nil {
		panic(err)
	}
	client := &http.Client{
		Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
		Timeout:   30 * time.Second,
	}

	resp, err := client.Get("https://api.ipify.org")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.StatusCode, string(body))
}

Rotating or sticky

The gateway gives a rotating login a new IP for every connection. A Transport keeps connections alive and reuses them, so two requests in a row usually share an IP. DisableKeepAlives: true makes every request open a new connection. With a -sid- key the IP stays the same on every connection until the -ttl- runs out (1 to 60 minutes).

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

// freshConn=true: a new connection for every request, so a rotating login gets a new IP each time
func newClient(login string, freshConn bool) *http.Client {
	proxyURL := &url.URL{Scheme: "http", User: url.UserPassword(login, "PASSWORD"), Host: "gate.aeonproxy.com:7000"}
	return &http.Client{
		Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL), DisableKeepAlives: freshConn},
		Timeout:   30 * time.Second,
	}
}

func ip(c *http.Client) string {
	resp, err := c.Get("https://api.ipify.org")
	if err != nil {
		return err.Error()
	}
	defer resp.Body.Close()
	b, _ := io.ReadAll(resp.Body)
	return string(b)
}

func main() {
	rotating := newClient("LOGIN-cc-us", true)
	fmt.Println(ip(rotating), ip(rotating)) // two IPs

	sticky := newClient("LOGIN-cc-us-sid-ab12cd34-ttl-30", true)
	fmt.Println(ip(sticky), ip(sticky)) // one IP, kept for 30 minutes
}

SOCKS5

proxy.SOCKS5 from golang.org/x/net/proxy returns a dialer that logs in with proxy.Auth. Plug its DialContext into a Transport. The dialer passes host names to the proxy, so DNS is resolved at the gateway, not on your machine.

package main

import (
	"fmt"
	"io"
	"net/http"
	"time"

	"golang.org/x/net/proxy"
)

func main() {
	auth := &proxy.Auth{User: "LOGIN-cc-de", Password: "PASSWORD"}
	dialer, err := proxy.SOCKS5("tcp", "gate.aeonproxy.com:7001", auth, proxy.Direct)
	if err != nil {
		panic(err)
	}
	client := &http.Client{
		Transport: &http.Transport{DialContext: dialer.(proxy.ContextDialer).DialContext},
		Timeout:   30 * time.Second,
	}

	resp, err := client.Get("https://api.ipify.org")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Without x/net, http.ProxyURL also takes socks5://LOGIN:[email protected]:7001, and it too sends host names to the proxy. Write socks5://: older Go releases treat socks5h:// as an HTTP proxy and fail. Neither way carries UDP; see SOCKS5 and UDP.

If it does not work

What you seeWhat to check
Proxy Authentication RequiredThe login, the password, or a key in the login that is not in the lists (a misspelled city, for example).
Payment RequiredNo traffic left on the product, or the sub-user reached its limit.
Bad GatewayThe exit failed. Retry: the next connection of a rotating login gets a new IP.
ForbiddenThe site is blocked by default for every account: mail ports, .gov and .mil sites, some banking and ticketing sites. See gateway errors.
socks connect ... username/password authentication failedThe gateway refused the SOCKS5 login. SOCKS5 has one failure code for every cause: check the login, the password, the keys and the traffic left.
malformed HTTP response "\x05\xff"A socks5h:// URL on an older Go. Use socks5://.
The same IP on every request with a rotating loginKeep-alive reuses the connection. Set DisableKeepAlives: true.

Questions

No. An http.Client is safe for concurrent use: create one per login and share it between goroutines. Bought traffic has no thread or connection limit on the gateway. Create several clients, each with its own -sid- key, when parallel workers each need their own sticky IP.

It went through a client without the proxy. http.Get and http.DefaultClient use http.ProxyFromEnvironment, which reads HTTPS_PROXY and HTTP_PROXY and goes direct when they are not set, and a Transport with no Proxy field always connects directly. Send the request through the client you built.

Proxy questions? Ask support in the live chat.