Documentation ¶
Overview ¶
Package geoip detects the country by an IP address and provides alternative handlers.
This package is compatible to IPv4 and IPv6. Uses the MaxMind database, or MaxMind WebService or alternative country/city detectors.
The detected country and all its attributes can be added to a context.
Index ¶
- Variables
- type Country
- type Finder
- type IsAllowedFunc
- type Option
- func OptionsError(err error) []Option
- func WithAllowedCountryCodes(isoCountryCodes []string, scopeIDs ...scope.TypeID) Option
- func WithAlternativeHandler(altHndlr mw.ErrorHandler, scopeIDs ...scope.TypeID) Option
- func WithAlternativeRedirect(urlStr string, code int, scopeIDs ...scope.TypeID) Option
- func WithCheckAllow(f IsAllowedFunc, scopeIDs ...scope.TypeID) Option
- func WithCountryFinder(cr Finder) Option
- func WithDebugLog(w io.Writer) Option
- func WithDefaultConfig(scopeIDs ...scope.TypeID) Option
- func WithDisable(isDisabled bool, scopeIDs ...scope.TypeID) Option
- func WithErrorHandler(eh mw.ErrorHandler, scopeIDs ...scope.TypeID) Option
- func WithLogger(l log.Logger) Option
- func WithMarkPartiallyApplied(partially bool, scopeIDs ...scope.TypeID) Option
- func WithOptionFactory(f OptionFactoryFunc) Option
- func WithRootConfig(cg config.Getter) Option
- func WithServiceErrorHandler(eh mw.ErrorHandler) Option
- type OptionFactories
- type OptionFactoryFunc
- type ScopedConfig
- type Service
- func (s *Service) ClearCache() error
- func (s *Service) Close() error
- func (s *Service) ConfigByScope(websiteID, storeID int64) (ScopedConfig, error)
- func (s *Service) ConfigByScopeID(current scope.TypeID, parent scope.TypeID) (scpCfg ScopedConfig, _ error)
- func (s *Service) ConfigByScopedGetter(scpGet config.Scoped) (ScopedConfig, error)
- func (s *Service) CountryByIP(r *http.Request) (*Country, error)
- func (s *Service) DebugCache(w io.Writer) error
- func (s *Service) Options(opts ...Option) error
- func (s *Service) WithCountryByIP(next http.Handler) http.Handler
- func (s *Service) WithIsCountryAllowedByIP(next http.Handler) http.Handler
- type StoreFinder
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultAlternativeHandler mw.ErrorHandler = mw.ErrorWithStatusCode(http.StatusServiceUnavailable)
DefaultAlternativeHandler gets called when detected Country cannot be found within the list of allowed countries. This handler can be overridden to provide a fallback for all scopes. To set a alternative handler for a website or store use the With*() options. This function gets called in WithIsCountryAllowedByIP.
Status is StatusServiceUnavailable
Functions ¶
This section is empty.
Types ¶
type Country ¶
type Country struct { // IP contains the request IP address even if we run behind a proxy IP net.IP `json:"ip,omitempty"` City struct { Confidence int `json:"confidence,omitempty"` GeoNameID uint `json:"geoname_id,omitempty"` Names map[string]string `json:"names,omitempty"` } `json:"city,omitempty"` Continent struct { Code string `json:"code,omitempty"` GeoNameID uint `json:"geoname_id,omitempty"` Names map[string]string `json:"names,omitempty"` } `json:"continent,omitempty"` Country struct { Confidence int `json:"confidence,omitempty"` GeoNameID uint `json:"geoname_id,omitempty"` IsoCode string `json:"iso_code,omitempty"` Names map[string]string `json:"names,omitempty"` } `json:"country,omitempty"` Location struct { AccuracyRadius int `json:"accuracy_radius,omitempty"` AverageIncome int `json:"average_income,omitempty"` Latitude float64 `json:"latitude,omitempty"` Longitude float64 `json:"longitude,omitempty"` MetroCode int `json:"metro_code,omitempty"` PopulationDensity int `json:"population_density,omitempty"` TimeZone string `json:"time_zone,omitempty"` } `json:"location,omitempty"` Postal struct { Code string `json:"code,omitempty"` Confidence int `json:"confidence,omitempty"` } `json:"postal,omitempty"` RegisteredCountry struct { GeoNameID uint `json:"geoname_id,omitempty"` IsoCode string `json:"iso_code,omitempty"` Names map[string]string `json:"names,omitempty"` } `json:"registered_country,omitempty"` RepresentedCountry struct { GeoNameID uint `json:"geoname_id,omitempty"` IsoCode string `json:"iso_code,omitempty"` Names map[string]string `json:"names,omitempty"` Type string `json:"type,omitempty"` } `json:"represented_country,omitempty"` Subdivision []struct { Confidence int `json:"confidence,omitempty"` GeoNameID uint `json:"geoname_id,omitempty"` IsoCode string `json:"iso_code,omitempty"` Names map[string]string `json:"names,omitempty"` } `json:"subdivisions,omitempty"` Traits struct { AutonomousSystemNumber int `json:"autonomous_system_number,omitempty"` AutonomousSystemOrganization string `json:"autonomous_system_organization,omitempty"` Domain string `json:"domain,omitempty"` IsAnonymousProxy bool `json:"is_anonymous_proxy,omitempty"` IsSatelliteProvider bool `json:"is_satellite_provider,omitempty"` Isp string `json:"isp,omitempty"` IPAddress string `json:"ip_address,omitempty"` Organization string `json:"organization,omitempty"` UserType string `json:"user_type,omitempty"` } `json:"traits,omitempty"` MaxMind struct { QueriesRemaining int `json:"queries_remaining,omitempty"` } `json:"maxmind,omitempty"` }
The Country structure corresponds to the data in the GeoIP2/GeoLite2 Country databases.
func FromContextCountry ¶
FromContextCountry returns the geoip.Country in ctx if it exists or an error. The error has been previously set by WithContextError. An error can be for the first request, with a new IP address to fill the cache, of behaviour NotValid but all subsequent requests are of behaviour NotFound.
type Finder ¶
type Finder interface { // Country todo add context for cancelling FindCountry(net.IP) (*Country, error) // Close closes the underlying object Close() error }
Finder finds a Country by an IP address. Supports IPv4 and IPv6 addresses. We call this find because focusing on getting a single, exact match.
type IsAllowedFunc ¶
IsAllowedFunc checks in middleware WithIsCountryAllowedByIP if the country is allowed to process the request. The StringSlice contains a list of ISO country names fetched from the config.ScopedGetter. Return nil to indicate that the request can continue.
type Option ¶
Option can be used as an argument in NewService to configure it with different settings.
func OptionsError ¶
OptionsError helper function to be used within the backend package or other sub-packages whose functions may return an OptionFactoryFunc.
func WithAllowedCountryCodes ¶
WithAllowedCountryCodes sets a list of ISO countries to be validated against. Only to be used with function WithIsCountryAllowedByIP()
func WithAlternativeHandler ¶
func WithAlternativeHandler(altHndlr mw.ErrorHandler, scopeIDs ...scope.TypeID) Option
WithAlternativeHandler sets for a scope the alternative handler if an IP address has been access denied. Only to be used with function WithIsCountryAllowedByIP()
func WithAlternativeRedirect ¶
WithAlternativeRedirect sets for a scope the error handler on a Service if an IP address has been access denied. Only to be used with function WithIsCountryAllowedByIP()
func WithCheckAllow ¶
func WithCheckAllow(f IsAllowedFunc, scopeIDs ...scope.TypeID) Option
WithCheckAllow sets your custom function which checks if the country of an IP address should access to granted, or the next middleware handler in the chain gets called. Only to be used with function WithIsCountryAllowedByIP()
func WithCountryFinder ¶
WithCountryFinder applies a custom CountryRetriever. Sets the retriever atomically and only once.
func WithDebugLog ¶
WithDebugLog creates a new standard library based logger with debug mode enabled. The passed writer must be thread safe.
func WithDefaultConfig ¶
WithDefaultConfig applies the default GeoIP configuration settings based for a specific scope. This function overwrites any previous set options.
Default values are:
- Alternative Handler: variable DefaultAlternativeHandler
- Logger black hole
- Check allow: If allowed countries are empty, all countries are allowed
func WithDisable ¶
WithDisable disables the current service and calls the next HTTP handler.
The variadic "scopeIDs" argument define to which scope the value gets applied and from which parent scope should be inherited. Setting no "scopeIDs" sets the value to the default scope. Setting one scope.TypeID defines the primary scope to which the value will be applied. Subsequent scope.TypeID are defining the fall back parent scopes to inherit the default or previously applied configuration from.
func WithErrorHandler ¶
func WithErrorHandler(eh mw.ErrorHandler, scopeIDs ...scope.TypeID) Option
WithErrorHandler adds a custom error handler. Gets called in the http.Handler after the scope can be extracted from the context.Context and the configuration has been found and is valid. The default error handler prints the error to the user and returns a http.StatusServiceUnavailable.
The variadic "scopeIDs" argument define to which scope the value gets applied and from which parent scope should be inherited. Setting no "scopeIDs" sets the value to the default scope. Setting one scope.TypeID defines the primary scope to which the value will be applied. Subsequent scope.TypeID are defining the fall back parent scopes to inherit the default or previously applied configuration from.
func WithLogger ¶
WithLogger convenient helper function to apply a logger to the Service type.
func WithMarkPartiallyApplied ¶
WithMarkPartiallyApplied if set to true marks a configuration for a scope as partially applied with functional options set via source code. The internal service knows that it must trigger additionally the OptionFactoryFunc to load configuration from a backend. Useful in the case where parts of the configurations are coming from backend storages and other parts like http handler have been set via code. This function should only be applied in case you work with WithOptionFactory().
The variadic "scopeIDs" argument define to which scope the value gets applied and from which parent scope should be inherited. Setting no "scopeIDs" sets the value to the default scope. Setting one scope.TypeID defines the primary scope to which the value will be applied. Subsequent scope.TypeID are defining the fall back parent scopes to inherit the default or previously applied configuration from.
func WithOptionFactory ¶
func WithOptionFactory(f OptionFactoryFunc) Option
WithOptionFactory applies a function which lazily loads the options from a slow backend (config.Getter) depending on the incoming scope within a request. For example applies the backend configuration to the service.
Once this option function has been set all other manually set option functions, which accept a scope and a scope ID as an argument, will NOT be overwritten by the new values retrieved from the configuration service.
cfgStruct, err := backendgeoip.NewConfigStructure() if err != nil { panic(err) } be := backendgeoip.New(cfgStruct) srv := geoip.MustNewService( geoip.WithOptionFactory(be.PrepareOptions()), )
func WithRootConfig ¶
WithRootConfig sets the root configuration service to retrieve the scoped base configuration. If you set the option WithOptionFactory() then the option WithRootConfig() does not need to be set as it won't get used.
func WithServiceErrorHandler ¶
func WithServiceErrorHandler(eh mw.ErrorHandler) Option
WithServiceErrorHandler sets the error handler on the Service object. Convenient helper function.
type OptionFactories ¶
type OptionFactories struct {
// contains filtered or unexported fields
}
OptionFactories allows to register multiple OptionFactoryFunc identified by their names. Those OptionFactoryFuncs will be loaded in the backend package depending on the configured name under a certain path. This type is embedded in the backendgeoip.Configuration type.
func NewOptionFactories ¶
func NewOptionFactories() *OptionFactories
NewOptionFactories creates a new struct and initializes the internal map for the registration of different option factories.
func (*OptionFactories) Deregister ¶
func (of *OptionFactories) Deregister(name string)
Deregister removes a functional option factory from the internal register.
func (*OptionFactories) Lookup ¶
func (of *OptionFactories) Lookup(name string) (OptionFactoryFunc, error)
Lookup returns a functional option factory identified by name or an error if the entry doesn't exists. May return a NotFound error behaviour.
func (*OptionFactories) Names ¶
func (of *OptionFactories) Names() []string
Names returns an unordered list of names of all registered functional option factories.
func (*OptionFactories) Register ¶
func (of *OptionFactories) Register(name string, factory OptionFactoryFunc)
Register adds another functional option factory to the internal register. Overwrites existing entries.
type OptionFactoryFunc ¶
OptionFactoryFunc a closure around a scoped configuration to figure out which options should be returned depending on the scope brought to you during a request.
type ScopedConfig ¶
type ScopedConfig struct { // AllowedCountries a slice which contains all allowed countries. An // incoming request for a scope checks if the country for an IP is contained // within this slice. Empty slice means that all countries are allowed. The // slice is owned by the callee. AllowedCountries []string // IsAllowedFunc checks in middleware WithIsCountryAllowedByIP if the country is // allowed to process the request. IsAllowedFunc // func(s scope.Hash, c *Country, allowedCountries []string) error // AlternativeHandler if ip/country is denied we call this handler. AlternativeHandler mw.ErrorHandler // contains filtered or unexported fields }
ScopedConfig scoped based configuration and should not be embedded into your own types. Call ScopedConfig.ScopeID to know to which scope this configuration has been bound to.
func (*ScopedConfig) IsAllowed ¶
func (sc *ScopedConfig) IsAllowed(c *Country) error
IsAllowed checks if the country is allowed. An empty AllowedCountries fields allows all countries.
type Service ¶
type Service struct { // Finder finds a country by an IP address. If nil panics during execution // in the middleware. This field gets protected by a mutex to allowing // setting the field during requests. Finder // contains filtered or unexported fields }
Service represents a service manager for GeoIP detection and restriction. Please consider the law in your country if you would like to implement geo-blocking.
func (*Service) ClearCache ¶
ClearCache clears the internal map storing all scoped configurations. You must reapply all functional options. TODO(CyS) all previously applied options will be automatically reapplied.
func (*Service) Close ¶
Close closes the underlying GeoIP CountryRetriever service and resets the internal loading state of the GeoIP flag. It does not yet clear the internal cache.
func (*Service) ConfigByScope ¶
func (s *Service) ConfigByScope(websiteID, storeID int64) (ScopedConfig, error)
ConfigByScope creates a new scoped configuration depending on the Service.useWebsite flag. If useWebsite==true the scoped configuration contains only the website->default scope despite setting a store scope. If an OptionFactory is set the configuration gets loaded from the backend. A nil root config causes a panic.
func (*Service) ConfigByScopeID ¶
func (s *Service) ConfigByScopeID(current scope.TypeID, parent scope.TypeID) (scpCfg ScopedConfig, _ error)
ConfigByScopeID returns the correct configuration for a scope and may fall back to the next higher scope: store -> website -> default. If `current` TypeID is Store, then the `parent` can only be Website or Default. If an entry for a scope cannot be found the next higher scope gets looked up and the pointer of the next higher scope gets assigned to the current scope. This prevents redundant configurations and enables us to change one scope configuration with an impact on all other scopes which depend on the parent scope. A zero `parent` triggers no further look ups. This function does not load any configuration (config.Getter related) from the backend and accesses the internal map of the Service directly.
Important: a "current" scope cannot have multiple "parent" scopes.
func (*Service) ConfigByScopedGetter ¶
func (s *Service) ConfigByScopedGetter(scpGet config.Scoped) (ScopedConfig, error)
ConfigByScopedGetter returns the internal configuration depending on the ScopedGetter. Mainly used within the middleware. If you have applied the option WithOptionFactory() the configuration will be pulled out only one time from the backend configuration service. The field optionInflight handles the guaranteed atomic single loading for each scope.
func (*Service) CountryByIP ¶
CountryByIP searches a country by an IP address and returns the found country. It only needs the functional options WithGeoIP*().
func (*Service) DebugCache ¶
DebugCache uses Sprintf to write an ordered list (by scope.TypeID) into a writer. Only usable for debugging.
func (*Service) WithCountryByIP ¶
WithCountryByIP is a simple middleware which detects the country via an IP address. With the detected country a new tree context.Context gets created. Use FromContextCountry() to extract the country or an error. If you don't like the middleware consider using the function CountryByIP().
func (*Service) WithIsCountryAllowedByIP ¶
WithIsCountryAllowedByIP queries the AllowedCountries slice to retrieve a list of countries for a scope and then uses the function IsAllowedFunc to check if a country is allowed for an IP address. If a country should not access the next handler within the middleware chain it will call an alternative handler to e.g. show a different page or perform a redirect. Use FromContextCountry() to extract the country or an error. Tis middleware allows geo blocking.
Example ¶
package main import ( "fmt" "net/http" "net/http/httptest" "path/filepath" "github.com/corestoreio/log" "github.com/corestoreio/pkg/config/cfgmock" "github.com/corestoreio/pkg/net/geoip" "github.com/corestoreio/pkg/net/geoip/backendgeoip" "github.com/corestoreio/pkg/net/geoip/maxmindfile" "github.com/corestoreio/pkg/net/mw" "github.com/corestoreio/pkg/store/scope" ) func main() { // The scope. Those two values are now hard coded because we cannot access // here the database to the website, store_group and store tables. const ( websiteID = 1 storeID = 2 ) logBuf := new(log.MutexBuffer) cfgStruct, err := backendgeoip.NewConfigStructure() if err != nil { panic(fmt.Sprintf("%+v", err)) } backend := backendgeoip.New(cfgStruct) backend.Register(maxmindfile.NewOptionFactory(backend.MaxmindLocalFile)) // This configuration says that any incoming request whose IP address does // not belong to the countries Germany (DE), Austria (AT) or Switzerland // (CH) gets redirected to the URL byebye.de.io with the redirect code 307. // // We're using here the cfgmock.NewService which is an in-memory // configuration service. Normally you would use the MySQL database or // consul or etcd. cfgSrv := cfgmock.NewService(cfgmock.PathValue{ // @see structure.go why scope.Store and scope.Website can be used. backend.DataSource.MustFQ(): `file`, // file triggers the lookup in backend.MaxmindLocalFile.MustFQ(): filepath.Join("testdata", "GeoIP2-Country-Test.mmdb"), backend.AlternativeRedirect.MustFQStore(storeID): `https://byebye.de.io`, backend.AlternativeRedirectCode.MustFQWebsite(websiteID): 307, backend.AllowedCountries.MustFQStore(storeID): "DE,AT,CH", }) geoSrv := geoip.MustNew( geoip.WithRootConfig(cfgSrv), geoip.WithDebugLog(logBuf), geoip.WithOptionFactory(backend.PrepareOptionFactory()), // Just for testing and in this example, we let the HTTP Handler panicking on any // error. You should not do that in production apps. geoip.WithServiceErrorHandler(mw.ErrorWithPanic), ) // Set up the incoming request from the outside world. The scope in the // context gets set via scope.RunModeCalculater or via JSON web token or via // session. req := httptest.NewRequest("GET", "https://corestore.io/", nil) req = req.WithContext(scope.WithContext(req.Context(), websiteID, storeID)) req.RemoteAddr = `2a02:d180::` // IP address range of Germany rec := httptest.NewRecorder() lastHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c, ok := geoip.FromContextCountry(r.Context()) if !ok { panic("A programmer made an error") } fmt.Fprintf(w, "Got allowed country DE:%s EN:%s", c.Country.Names["de"], c.Country.Names["en"]) }) geoSrv.WithIsCountryAllowedByIP(lastHandler).ServeHTTP(rec, req) fmt.Println(rec.Body.String()) // Change the request to an IP address outside the DACH region req.RemoteAddr = `2a02:d200::` // Finland rec = httptest.NewRecorder() // re-run the middleware and access is denied despite the scope in the context // is the same. geoSrv.WithIsCountryAllowedByIP(lastHandler).ServeHTTP(rec, req) fmt.Println(rec.Body.String()) }
Output: Got allowed country DE:Deutschland EN:Germany <a href="https://byebye.de.io">Temporary Redirect</a>.
type StoreFinder ¶
type StoreFinder interface { // DefaultStoreID returns the default active store ID and its website ID // depending on the run mode. Error behaviour is mostly of type NotValid. DefaultStoreID(runMode scope.TypeID) (websiteID, storeID uint32, err error) // StoreIDbyCode returns, depending on the runMode, for a storeCode its // active store ID and its website ID. An empty runMode hash falls back to // select the default website with its default group and the slice of // default stores. A not-found error behaviour gets returned if the code // cannot be found. If the runMode equals to scope.DefaultTypeID, the returned // ID is always 0 and error is nil. StoreIDbyCode(runMode scope.TypeID, storeCode string) (websiteID, storeID uint32, err error) }
StoreFinder see store.Finder for a description. Usage of this interface in WithRunMode() middleware.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package backendgeoip defines the backend configuration options and element slices.
|
Package backendgeoip defines the backend configuration options and element slices. |
Package maxmindfile provides an OptionFactoryFunc for the backendgeopip package.
|
Package maxmindfile provides an OptionFactoryFunc for the backendgeopip package. |
Package maxmindwebservice provides an OptionFactoryFunc for the backendgeopip package.
|
Package maxmindwebservice provides an OptionFactoryFunc for the backendgeopip package. |