mirror of
https://github.com/lightninglabs/aperture.git
synced 2025-12-19 01:54:20 +01:00
multi: add and use DynamicPrice option
This commit adds a DynamicPrice member to the Services struct and uses its values to determine if a GRPCPricer or DefaultPricer should be initialised. The commit also updates the sample-conf.yaml file with the new config options.
This commit is contained in:
@@ -104,21 +104,50 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
resourceName := target.ResourceName(r.URL.Path)
|
||||
|
||||
// Determine auth level required to access service and dispatch request
|
||||
// accordingly.
|
||||
authLevel := target.AuthRequired(r)
|
||||
switch {
|
||||
case authLevel.IsOn():
|
||||
if !p.authenticator.Accept(&r.Header, target.Name) {
|
||||
// Determine if the header contains the authentication
|
||||
// required for the given resource. The call to Accept is
|
||||
// called in each case body rather than outside the switch so
|
||||
// as to avoid calling this possibly expensive call for static
|
||||
// resources.
|
||||
acceptAuth := p.authenticator.Accept(&r.Header, resourceName)
|
||||
if !acceptAuth {
|
||||
price, err := target.pricer.GetPrice(
|
||||
r.Context(), r.URL.Path,
|
||||
)
|
||||
if err != nil {
|
||||
prefixLog.Errorf("error getting "+
|
||||
"resource price: %v", err)
|
||||
sendDirectResponse(
|
||||
w, r, http.StatusInternalServerError,
|
||||
"failure fetching "+
|
||||
"resource price",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// If the price returned is zero, then break out of the
|
||||
// switch statement and allow access to the service.
|
||||
if price == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
prefixLog.Infof("Authentication failed. Sending 402.")
|
||||
p.handlePaymentRequired(w, r, target.Name, target.Price)
|
||||
p.handlePaymentRequired(w, r, resourceName, price)
|
||||
return
|
||||
}
|
||||
|
||||
case authLevel.IsFreebie():
|
||||
// We only need to respect the freebie counter if the user
|
||||
// is not authenticated at all.
|
||||
if !p.authenticator.Accept(&r.Header, target.Name) {
|
||||
acceptAuth := p.authenticator.Accept(&r.Header, resourceName)
|
||||
if !acceptAuth {
|
||||
ok, err := target.freebieDb.CanPass(r, remoteIP)
|
||||
if err != nil {
|
||||
prefixLog.Errorf("Error querying freebie db: "+
|
||||
@@ -130,7 +159,30 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
p.handlePaymentRequired(w, r, target.Name, target.Price)
|
||||
price, err := target.pricer.GetPrice(
|
||||
r.Context(), r.URL.Path,
|
||||
)
|
||||
if err != nil {
|
||||
prefixLog.Errorf("error getting "+
|
||||
"resource price: %v", err)
|
||||
sendDirectResponse(
|
||||
w, r, http.StatusInternalServerError,
|
||||
"failure fetching "+
|
||||
"resource price",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// If the price returned is zero, then break
|
||||
// out of the switch statement and allow access
|
||||
// to the service.
|
||||
if price == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
p.handlePaymentRequired(
|
||||
w, r, resourceName, target.Price,
|
||||
)
|
||||
return
|
||||
}
|
||||
_, err = target.freebieDb.TallyFreebie(r, remoteIP)
|
||||
@@ -188,7 +240,16 @@ func (p *Proxy) UpdateServices(services []*Service) error {
|
||||
|
||||
// Close cleans up the Proxy by closing any remaining open connections.
|
||||
func (p *Proxy) Close() error {
|
||||
return nil
|
||||
var returnErr error
|
||||
for _, s := range p.services {
|
||||
if err := s.pricer.Close(); err != nil {
|
||||
log.Errorf("error while closing the pricer of "+
|
||||
"service %s: %v", s.Name, err)
|
||||
returnErr = err
|
||||
}
|
||||
}
|
||||
|
||||
return returnErr
|
||||
}
|
||||
|
||||
// director is a method that rewrites an incoming request to be forwarded to a
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/aperture/auth"
|
||||
"github.com/lightninglabs/aperture/freebie"
|
||||
"github.com/lightninglabs/aperture/pricer"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -84,6 +85,10 @@ type Service struct {
|
||||
// service's endpoint.
|
||||
Price int64 `long:"price" description:"Static LSAT value in satoshis to be used for this service"`
|
||||
|
||||
// DynamicPrice holds the config options needed for initialising
|
||||
// the pricer if a gPRC server is to be used for price data.
|
||||
DynamicPrice pricer.Config `long:"dynamicprice" description:"Configuration for connecting to the gRPC server to use for the pricer backend"`
|
||||
|
||||
// AuthWhitelistPaths is an optional list of regular expressions that
|
||||
// are matched against the path of the URL of a request. If the request
|
||||
// URL matches any of those regular expressions, the call is treated as
|
||||
@@ -93,6 +98,20 @@ type Service struct {
|
||||
AuthWhitelistPaths []string `long:"authwhitelistpaths" description:"List of regular expressions for paths that don't require authentication'"`
|
||||
|
||||
freebieDb freebie.DB
|
||||
pricer pricer.Pricer
|
||||
}
|
||||
|
||||
// ResourceName returns the string to be used to identify which resource a
|
||||
// macaroon has access to. If DynamicPrice Enabled option is set to true then
|
||||
// the service has further restrictions per resource and so the name will
|
||||
// include both the service name and the specific resource name. Otherwise
|
||||
// authorisation is only restricted by service name.
|
||||
func (s *Service) ResourceName(resourcePath string) string {
|
||||
if s.DynamicPrice.Enabled {
|
||||
return fmt.Sprintf("%s%s", s.Name, resourcePath)
|
||||
}
|
||||
|
||||
return s.Name
|
||||
}
|
||||
|
||||
// AuthRequired determines the auth level required for a given request.
|
||||
@@ -170,6 +189,22 @@ func prepareServices(services []*Service) error {
|
||||
}
|
||||
}
|
||||
|
||||
// If dynamic prices are enabled then use the provided
|
||||
// DynamicPrice options to initialise a gRPC backed
|
||||
// pricer client.
|
||||
if service.DynamicPrice.Enabled {
|
||||
priceClient, err := pricer.NewGRPCPricer(
|
||||
&service.DynamicPrice,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initializing "+
|
||||
"pricer: %v", err)
|
||||
}
|
||||
|
||||
service.pricer = priceClient
|
||||
continue
|
||||
}
|
||||
|
||||
// Check that the price for the service is not negative and not
|
||||
// more than the maximum amount allowed by lnd. If no price, or
|
||||
// a price of zero satoshis, is set the then default price of 1
|
||||
@@ -186,6 +221,10 @@ func prepareServices(services []*Service) error {
|
||||
return fmt.Errorf("maximum price exceeded for "+
|
||||
"service %s", service.Name)
|
||||
}
|
||||
|
||||
// Initialise a default pricer where all resources in a server
|
||||
// are given the same price.
|
||||
service.pricer = pricer.NewDefaultPricer(service.Price)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -82,8 +82,26 @@ services:
|
||||
constraints:
|
||||
"valid_until": "2020-01-01"
|
||||
|
||||
# The LSAT value in satoshis for the service.
|
||||
price: 1
|
||||
# The LSAT value in satoshis for the service. It is ignored if
|
||||
# dynamicprice.enabled is set to true.
|
||||
price: 0
|
||||
|
||||
# Options to use for connection to the price serving gRPC server.
|
||||
dynamicprice:
|
||||
# Whether or not a gRPC server is available to query price data from. If
|
||||
# this option is set to true then the 'price' option is ignored.
|
||||
enabled: true
|
||||
|
||||
# The address of the gRPC pricer server.
|
||||
grpcaddress: "127.0.0.1:10010"
|
||||
|
||||
# Whether or not TLS encryption should be used for communications with the
|
||||
# gRPC server.
|
||||
insecure: false
|
||||
|
||||
# The path to the pricer server's tls.cert. If the 'insecure' option is
|
||||
# set to true then this path must be set.
|
||||
tlscertpath: "path-to-pricer-server-tls-cert/tls.cert"
|
||||
|
||||
- name: "service2"
|
||||
hostregexp: "service2.com:8083"
|
||||
@@ -94,6 +112,19 @@ services:
|
||||
"valid_until": "2020-01-01"
|
||||
price: 1
|
||||
|
||||
- name: "service3"
|
||||
hostregexp: "service3.com:8083"
|
||||
pathregexp: '^/.*$'
|
||||
address: "123.456.789:8082"
|
||||
protocol: https
|
||||
constraints:
|
||||
"valid_until": "2020-01-01"
|
||||
dynamicprice:
|
||||
enbled: true
|
||||
grpcaddress: 123.456.789:8083
|
||||
insecure: false
|
||||
tlscertpath: "path-to-pricer-server-tls-cert/tls.cert"
|
||||
|
||||
# Settings for a Tor instance to allow requests over Tor as onion services.
|
||||
# Configuring Tor is optional.
|
||||
tor:
|
||||
|
||||
Reference in New Issue
Block a user