Documentation
¶
Overview ¶
Package nodemaven builds NodeMaven proxy credentials, checks a connection through the gateway, and wraps the dashboard account API.
A Proxy is one gateway identity. New validates every parameter before anything is sent, because the gateway answers an unknown parameter name with 200 and drops it:
proxy, err := nodemaven.New(
nodemaven.WithLogin("your-login"),
nodemaven.WithPassword("your-password"),
nodemaven.WithParam("country", "us"),
)
client := &http.Client{Transport: &http.Transport{Proxy: proxy.HTTPProxy()}}
Only Proxy.Check and the Client methods open connections. Errors are *Error values tested with errors.Is against ErrParam, ErrCredentials, ErrProvider, ErrCheck, ErrAPI, ErrAuth, ErrNotFound and ErrRateLimit.
The gateway's dialect - separators, known names, value folding and what its statuses mean - is data in a TOML definition. The NodeMaven one is built in; LoadFile reads another.
Example ¶
package main
import (
"fmt"
"log"
"net/http"
"github.com/nodemaven/nodemaven-go"
)
func main() {
proxy, err := nodemaven.New(
nodemaven.WithLogin("your-login"),
nodemaven.WithPassword("your-password"),
nodemaven.WithParam("country", "us"),
nodemaven.WithParam("sid", "order4417"),
)
if err != nil {
log.Fatal(err)
}
fmt.Println(proxy.Username())
client := &http.Client{Transport: &http.Transport{Proxy: proxy.HTTPProxy()}}
_ = client // client.Get("https://example.com") leaves from one sticky exit
}
Output: your-login-country-us-sid-order4417
Index ¶
- Constants
- Variables
- func Available() []string
- type BrowserProxy
- type Check
- type Client
- func (c *Client) All(ctx context.Context, page *Page) ([]any, error)
- func (c *Client) Cities(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) Countries(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) CreateSubUser(ctx context.Context, proxyUsername, proxyPassword string, extra map[string]any) (any, error)
- func (c *Client) DeleteSubUser(ctx context.Context, id any) (any, error)
- func (c *Client) DeleteWhitelistIP(ctx context.Context, ipID any) (any, error)
- func (c *Client) DomainStatistics(ctx context.Context, proxyUsername string, filters url.Values) (*Page, error)
- func (c *Client) GoString() string
- func (c *Client) ISPCities(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) ISPRegions(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) ISPs(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) Iterate(ctx context.Context, page *Page) *Rows
- func (c *Client) Me(ctx context.Context) (map[string]any, error)
- func (c *Client) Regions(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) ResetSubUserUsage(ctx context.Context, ids []any) (any, error)
- func (c *Client) StatisticsData(ctx context.Context, proxyUsername string, filters url.Values) (map[string]any, error)
- func (c *Client) StatisticsRequests(ctx context.Context, proxyUsername string, filters url.Values) (map[string]any, error)
- func (c *Client) String() string
- func (c *Client) SubUsers(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) UpdateSubUser(ctx context.Context, id any, changes map[string]any) (any, error)
- func (c *Client) UpsertWhitelistIP(ctx context.Context, ip string, portsCount int, extra map[string]any) (map[string]any, error)
- func (c *Client) Validate(ctx context.Context, proxy *Proxy) ([]string, error)
- func (c *Client) WhitelistIP(ctx context.Context, ipID any) (map[string]any, error)
- func (c *Client) WhitelistIPs(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) ZipCodeCities(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) ZipCodeRegions(ctx context.Context, filters url.Values) (*Page, error)
- func (c *Client) ZipCodes(ctx context.Context, filters url.Values) (*Page, error)
- type ClientOption
- type Connect
- type Error
- type Header
- type Option
- type Page
- type Paging
- type Param
- type Provider
- func (p *Provider) Allowed(name string) []string
- func (p *Provider) ExitIPHeaders() []string
- func (p *Provider) Host() string
- func (p *Provider) ID() string
- func (p *Provider) IsMeasured() bool
- func (p *Provider) KnownParams() []string
- func (p *Provider) Label() string
- func (p *Provider) Normalized(name, value string) string
- func (p *Provider) Normalizes(name string) bool
- func (p *Provider) Notes() string
- func (p *Provider) PairSeparator() string
- func (p *Provider) Port() int
- func (p *Provider) Reaction(status int) string
- func (p *Provider) Separator() string
- func (p *Provider) SessionParam() string
- func (p *Provider) Spell(name string) string
- func (p *Provider) Status() string
- type ProviderSpec
- type Proxy
- func (p *Proxy) Browser() BrowserProxy
- func (p *Proxy) Check(ctx context.Context) (*Check, error)
- func (p *Proxy) Connect() Connect
- func (p *Proxy) GoString() string
- func (p *Proxy) HTTPProxy() func(*http.Request) (*url.URL, error)
- func (p *Proxy) Param(name string) (string, bool)
- func (p *Proxy) Params() []Param
- func (p *Proxy) Provider() *Provider
- func (p *Proxy) ProxyURL() *url.URL
- func (p *Proxy) Server() string
- func (p *Proxy) Session(id string) (*Proxy, error)
- func (p *Proxy) Sessions(count int) ([]*Proxy, error)
- func (p *Proxy) SessionsOfLength(count, length int) ([]*Proxy, error)
- func (p *Proxy) String() string
- func (p *Proxy) URL() string
- func (p *Proxy) URLWithScheme(scheme string) string
- func (p *Proxy) Username() string
- func (p *Proxy) With(name, value string) (*Proxy, error)
- func (p *Proxy) Without(name string) (*Proxy, error)
- type Rows
Examples ¶
Constants ¶
const APIRoot = "/api/v2/base"
APIRoot is the path every account API route starts with.
const ASCIIWhitespace = " \t\n\r\v\f"
ASCIIWhitespace is every character treated as whitespace in a parameter value. It is spelled out rather than taken from a standard library: Go's unicode.IsSpace also matches U+0085 and U+00A0, and the rule has to be the same in every NodeMaven SDK.
const DefaultAPITimeout = 30 * time.Second
DefaultAPITimeout bounds one request made by the built-in HTTP client.
const DefaultBaseURL = "https://dashboard.nodemaven.com"
DefaultBaseURL is the dashboard host the account API lives on.
const DefaultCheckTimeout = 15 * time.Second
DefaultCheckTimeout bounds each step of a check. One of the gateway's measured reactions is no reply for about 20 seconds, so a shorter default would report a parameter mistake as a network problem.
const DefaultPageSize = 1000
DefaultPageSize is the largest limit the location endpoints accept. It is a ceiling on one answer, not a count of the rows behind it; Iterate walks the rest.
const DefaultProvider = "nodemaven"
DefaultProvider is the id of the definition used when none is given.
const DefaultTarget = "api.ipify.org:443"
DefaultTarget is the host a check asks the gateway to tunnel to. Whatever is named sees a real TCP connection from the exit address.
Variables ¶
var ( // ByOffset pages the location catalogue: limit and offset, from 0. ByOffset = Paging{SizeKey: "limit", CursorKey: "offset", CursorCountsRows: true, DefaultSize: DefaultPageSize} // ISPCitiesPaging is ByOffset at 100 rows, because that endpoint takes about // a minute to answer 1000. ISPCitiesPaging = Paging{SizeKey: "limit", CursorKey: "offset", CursorCountsRows: true, DefaultSize: 100} // ByPageNumber pages sub-users: per_page and page, from 1. ByPageNumber = Paging{SizeKey: "per_page", CursorKey: "page", FirstCursor: 1} // WhitelistPaging pages the IP whitelist: page_size and page, from 1, and a // 404 past the end. WhitelistPaging = Paging{SizeKey: "page_size", CursorKey: "page", DefaultSize: 100, FirstCursor: 1, EndsWithNotFound: true} )
var ( // ErrParam is input the gateway would silently ignore, misreport, or hang on. ErrParam = errors.New("nodemaven: parameter") // ErrCredentials is a missing or malformed login, password, host or port, // or a missing API key. ErrCredentials = errors.New("nodemaven: credentials") // ErrProvider is a provider definition that does not describe a gateway. ErrProvider = errors.New("nodemaven: provider") // ErrCheck is a CONNECT that produced no answer at all. A refused tunnel is // not this error: it comes back as a CheckResult carrying the status. ErrCheck = errors.New("nodemaven: check") // ErrAPI is an account API error: an error status, a body that is not // JSON, or no response at all. ErrAPI = errors.New("nodemaven: api") // ErrAuth is a 401 or 403 from the account API. ErrAuth = errors.New("nodemaven: auth") // ErrNotFound is a 404 from the account API, where a 404 does not end a // paged walk. ErrNotFound = errors.New("nodemaven: not found") // ErrRateLimit is a 429 from the account API. Nothing here retries. ErrRateLimit = errors.New("nodemaven: rate limit") )
The kinds of error this package returns. Test for them with errors.Is:
if errors.Is(err, nodemaven.ErrParam) { ... }
The names line up with the other NodeMaven SDKs: param, credentials, provider, check, api, auth, not_found, rate_limit. ErrAuth, ErrNotFound and ErrRateLimit are kinds of ErrAPI, so errors.Is(err, ErrAPI) is true for all four.
Functions ¶
Types ¶
type BrowserProxy ¶
BrowserProxy is the shape browser automation takes a proxy in: the server without credentials, and the credentials separately and not encoded.
func (BrowserProxy) GoString ¶
func (b BrowserProxy) GoString() string
GoString redacts the password.
type Check ¶
type Check struct {
// Status is the CONNECT status.
Status int
// Reason is the reason phrase, verbatim.
Reason string
// Server is the host:port that was asked.
Server string
// Elapsed includes DNS and the TCP handshake.
Elapsed time.Duration
// Headers holds every response header in the order received.
Headers []Header
// ExitIP is the exit address, or "" when the gateway did not send one.
// That is normal on this gateway, not an error.
ExitIP string
// Meaning is what this status means on this gateway, or "" when nobody
// has recorded it.
Meaning string
}
Check is the gateway's answer to one CONNECT. A refusal is a Check with a status other than 200, not an error: the status is the answer.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client talks to the account API. Build one with NewClient.
func NewClient ¶
func NewClient(opts ...ClientOption) (*Client, error)
NewClient builds an account API client.
func (*Client) Countries ¶
Countries lists the countries. filters are passed through in the server's own spelling, such as country__code; the server ignores a name it does not know, so a misspelt filter is dropped without a word.
func (*Client) CreateSubUser ¶
func (c *Client) CreateSubUser(ctx context.Context, proxyUsername, proxyPassword string, extra map[string]any) (any, error)
CreateSubUser creates a sub-user. extra adds fields such as traffic_limit.
func (*Client) DeleteSubUser ¶
DeleteSubUser deletes one sub-user.
func (*Client) DeleteWhitelistIP ¶
DeleteWhitelistIP removes a whitelisted address.
func (*Client) DomainStatistics ¶
func (c *Client) DomainStatistics(ctx context.Context, proxyUsername string, filters url.Values) (*Page, error)
DomainStatistics is traffic by domain for one proxy username. It does not page; the whole answer is one page.
func (*Client) ISPRegions ¶
ISPRegions lists the regions that have ISPs.
func (*Client) Iterate ¶
Iterate starts a walk from page.
Example ¶
package main
import (
"context"
"fmt"
"log"
"net/url"
"github.com/nodemaven/nodemaven-go"
)
func main() {
client, err := nodemaven.NewClient() // reads NODEMAVEN_APIKEY
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
page, err := client.Cities(ctx, url.Values{"country__code": {"us"}})
if err != nil {
log.Fatal(err)
}
rows := client.Iterate(ctx, page)
for rows.Next() {
city := rows.Row().(map[string]any)
fmt.Println(city["code"])
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
}
Output:
func (*Client) Me ¶
Me is the account. Its fields are the server's own; it carries a live proxy password, so do not log it.
func (*Client) ResetSubUserUsage ¶
ResetSubUserUsage resets the usage counters of the given sub-users.
func (*Client) StatisticsData ¶
func (c *Client) StatisticsData(ctx context.Context, proxyUsername string, filters url.Values) (map[string]any, error)
StatisticsData is traffic over time for one proxy username. Dates are dd-mm-yyyy; give start and end, or period, or the server answers 500.
func (*Client) StatisticsRequests ¶
func (c *Client) StatisticsRequests(ctx context.Context, proxyUsername string, filters url.Values) (map[string]any, error)
StatisticsRequests is request counts over time for one proxy username.
func (*Client) UpdateSubUser ¶
UpdateSubUser changes the given fields of one sub-user.
func (*Client) UpsertWhitelistIP ¶
func (c *Client) UpsertWhitelistIP(ctx context.Context, ip string, portsCount int, extra map[string]any) (map[string]any, error)
UpsertWhitelistIP creates or replaces a whitelisted address. protocol is always sent, as HTTP unless extra says otherwise, because the server does not apply the default its own document declares.
func (*Client) Validate ¶
Validate checks a proxy's parameters against the live catalogue and returns a sentence per problem, or none. It costs a request per catalogue page.
func (*Client) WhitelistIP ¶
WhitelistIP is one whitelisted address.
func (*Client) WhitelistIPs ¶
WhitelistIPs lists the whitelisted addresses.
func (*Client) ZipCodeCities ¶
ZipCodeCities lists the cities that have zip codes.
func (*Client) ZipCodeRegions ¶
ZipCodeRegions lists the regions that have zip codes.
type ClientOption ¶
type ClientOption func(*clientSettings)
ClientOption configures NewClient.
func WithAPIKey ¶
func WithAPIKey(key string) ClientOption
WithAPIKey sets the dashboard API key. Without it, NODEMAVEN_APIKEY is read. This is not the proxy password.
func WithBaseURL ¶
func WithBaseURL(base string) ClientOption
WithBaseURL points the client at another host. Without it, NODEMAVEN_BASE_URL is read, then DefaultBaseURL.
func WithHTTPClient ¶
func WithHTTPClient(client *http.Client) ClientOption
WithHTTPClient sends requests through your own client. The built-in one follows no redirect and reads no proxy from the environment, because the API key travels in a header; a client of your own decides both for itself. A redirect it follows to the dashboard's not-found page still comes back as an error, because that page is not JSON.
func WithTimeout ¶
func WithTimeout(timeout time.Duration) ClientOption
WithTimeout bounds each request made by the built-in HTTP client. It does not apply to a client passed with WithHTTPClient.
type Connect ¶
type Connect struct {
Server string
Username string
Password string
// Target is the host:port to tunnel to.
Target string
// Timeout bounds each step: resolving, connecting, writing, and each read.
// Zero is refused rather than read as "no limit".
Timeout time.Duration
// ExitIPHeaders are tried in order; the first one present wins.
ExitIPHeaders []string
// Reactions maps a status, as text, to what it means on this gateway.
Reactions map[string]string
}
Connect is one CONNECT request, before it is sent. Proxy.Connect returns one filled in from the proxy and its definition; NewConnect fills in the defaults for a gateway described by hand.
func NewConnect ¶
NewConnect describes a CONNECT to server with the default target and timeout.
type Error ¶
type Error struct {
Kind error
Status int
Message string
// contains filtered or unexported fields
}
Error is every error this package returns. Kind is one of the Err values above; Status is the HTTP status behind an account API error, or 0 when no status came back.
It carries no response body: two of the account API's endpoints return live proxy passwords, and an error's text goes wherever the caller's logs go.
type Option ¶
type Option func(*settings)
Option configures New.
func WithHost ¶
WithHost sets the gateway host. Without it, <ID>_HOST is read, then the definition's own host.
func WithParam ¶
WithParam sets one gateway parameter. Parameters go into the username in the order they are first set; setting a name again replaces its value in place.
func WithPassword ¶
WithPassword sets the proxy password. Without it, <ID>_PASSWORD is read.
func WithPort ¶
WithPort sets the gateway port. Without it, <ID>_PORT is read, then the definition's own port.
func WithProvider ¶
WithProvider uses a definition other than the shipped NodeMaven one.
type Page ¶
type Page struct {
Rows []any
// Count, Next and Previous are the server's, and are empty on every
// endpoint measured so far.
Count *int64
Next string
Previous string
// contains filtered or unexported fields
}
Page is one answer from a list endpoint. Rows are decoded JSON, numbers as json.Number, objects as map[string]any.
type Paging ¶
type Paging struct {
SizeKey string
CursorKey string
CursorCountsRows bool
// DefaultSize is sent when the caller sets no size; 0 sends none.
DefaultSize int
// FirstCursor is sent when the caller sets no cursor.
FirstCursor int
// EndsWithNotFound marks an endpoint that answers 404 past its last page.
EndsWithNotFound bool
}
Paging is how one endpoint pages: which query parameter sizes a page, which one moves the cursor, and how the cursor counts.
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider describes one gateway's username dialect: its separators, the parameter names it recognises, how values are folded, and what its CONNECT statuses mean. Build one with Load, LoadFile or LoadString, or with NewProvider for a definition written in code.
func LoadFile ¶
LoadFile reads a definition from a TOML file. Its id is the file name without the extension.
func LoadFileAs ¶
LoadFileAs reads a definition from a TOML file under the given id.
func LoadString ¶
LoadString reads a definition from TOML text.
func NewProvider ¶
func NewProvider(spec ProviderSpec) (*Provider, error)
NewProvider builds a definition in code and runs the same consistency checks a definition read from a file goes through.
func (*Provider) Allowed ¶
Allowed lists the legal values of a parameter, or nil when nobody has established them and any value is passed through.
func (*Provider) ExitIPHeaders ¶
ExitIPHeaders lists every response header the exit address may arrive on, in the order they are tried.
func (*Provider) IsMeasured ¶
IsMeasured reports whether Status is "measured".
func (*Provider) KnownParams ¶
KnownParams lists the recognised parameter names, sorted.
func (*Provider) Normalized ¶
Normalized is the wire form of a value: for a folded parameter, trimmed of ASCII whitespace, A-Z lowered to a-z and nothing else, and each space turned into an underscore. Other parameters are returned unchanged.
func (*Provider) Normalizes ¶
Normalizes reports whether values of this parameter are folded.
func (*Provider) PairSeparator ¶
PairSeparator joins a parameter's name to its value.
func (*Provider) Reaction ¶
Reaction is what a CONNECT status means on this gateway, or "" when nobody has recorded one.
func (*Provider) SessionParam ¶
SessionParam names the sticky-session parameter, or "" when there is none.
type ProviderSpec ¶
type ProviderSpec struct {
ID string
Label string
KnownParams []string
Status string
Prefix string
Separator string
PairSeparator string
SessionParam string
Host string
Port int
Aliases map[string]string
Values map[string][]string
Normalize []string
ConnectReactions map[int]string
ExitIPHeaders []string
Notes string
}
ProviderSpec describes a definition written in code. Zero values take the same defaults as a file that leaves the key out.
type Proxy ¶
type Proxy struct {
// contains filtered or unexported fields
}
Proxy is one gateway identity: credentials, address and parameters. It is immutable; every method that changes something returns a new Proxy. Nothing here opens a connection except Check.
func New ¶
New builds a proxy and validates every parameter against the definition. Credentials have to be present, never valid: nothing is sent.
Example (Refusal) ¶
package main
import (
"errors"
"fmt"
"github.com/nodemaven/nodemaven-go"
)
func main() {
_, err := nodemaven.New(
nodemaven.WithLogin("your-login"),
nodemaven.WithPassword("your-password"),
nodemaven.WithParam("contry", "us"),
)
fmt.Println(errors.Is(err, nodemaven.ErrParam))
}
Output: true
func (*Proxy) Browser ¶
func (p *Proxy) Browser() BrowserProxy
Browser is the proxy for Playwright, chromedp, Rod and the like.
func (*Proxy) Check ¶
Check opens one CONNECT through the gateway to DefaultTarget and returns its answer. It is the only call on a Proxy that opens a socket.
Example ¶
package main
import (
"context"
"fmt"
"log"
"github.com/nodemaven/nodemaven-go"
)
func main() {
proxy, err := nodemaven.New(nodemaven.WithParam("country", "us"))
if err != nil {
log.Fatal(err)
}
check, err := proxy.Check(context.Background())
if err != nil {
log.Fatal(err) // nothing came back at all
}
fmt.Println(check) // a refusal is a status, with what it means on this gateway
}
Output:
func (*Proxy) Connect ¶
Connect is the CONNECT a Check would send, for changing its target or timeout before sending it.
func (*Proxy) HTTPProxy ¶
HTTPProxy is a value for http.Transport.Proxy that sends every request through this proxy:
client := &http.Client{Transport: &http.Transport{Proxy: proxy.HTTPProxy()}}
func (*Proxy) Sessions ¶
Sessions returns count copies, each pinned to its own random session of 12 hexadecimal characters.
Example ¶
package main
import (
"fmt"
"log"
"github.com/nodemaven/nodemaven-go"
)
func main() {
proxy, err := nodemaven.New(
nodemaven.WithLogin("your-login"),
nodemaven.WithPassword("your-password"),
nodemaven.WithParam("country", "de"),
)
if err != nil {
log.Fatal(err)
}
pool, err := proxy.Sessions(10)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(pool))
}
Output: 10
func (*Proxy) SessionsOfLength ¶
SessionsOfLength is Sessions with ids of 2*length characters drawn from the operating system's random source. Ids are distinct within one call.
func (*Proxy) URL ¶
URL is http://user:pass@host:port with both credentials percent-encoded.
func (*Proxy) URLWithScheme ¶
URLWithScheme is URL with another scheme, such as socks5.
type Rows ¶
type Rows struct {
// MaxPages bounds the walk. Set it before the first call to Next.
MaxPages int
// contains filtered or unexported fields
}
Rows walks a collection row by row from the page given, asking for the following pages as it goes:
rows := client.Iterate(ctx, page)
for rows.Next() {
use(rows.Row())
}
if err := rows.Err(); err != nil { ... }
It stops with an error rather than loop: past MaxPages pages, on a next link it has already followed, on a page identical to the one before, and on a next link to another host, because that request would carry the API key.