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.Authwith 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 defaultGOTOOLCHAIN=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.
- 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 see | What to check |
|---|---|
Proxy Authentication Required | The login, the password, or a key in the login that is not in the lists (a misspelled city, for example). |
Payment Required | No traffic left on the product, or the sub-user reached its limit. |
Bad Gateway | The exit failed. Retry: the next connection of a rotating login gets a new IP. |
Forbidden | The 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 failed | The 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 login | Keep-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.