2018-10-13 14:27:03 +00:00
|
|
|
package ipfsproxy
|
|
|
|
|
|
|
|
import (
|
2018-10-14 10:37:42 +00:00
|
|
|
"encoding/json"
|
2018-10-13 14:27:03 +00:00
|
|
|
"errors"
|
2018-10-14 10:37:42 +00:00
|
|
|
"fmt"
|
2018-10-13 14:27:03 +00:00
|
|
|
"time"
|
|
|
|
|
2018-12-17 02:54:17 +00:00
|
|
|
"github.com/kelseyhightower/envconfig"
|
2018-10-13 14:27:03 +00:00
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
2018-10-14 17:12:50 +00:00
|
|
|
|
|
|
|
"github.com/ipfs/ipfs-cluster/config"
|
2018-10-13 14:27:03 +00:00
|
|
|
)
|
|
|
|
|
2018-12-17 02:54:17 +00:00
|
|
|
const (
|
|
|
|
configKey = "ipfsproxy"
|
|
|
|
envConfigKey = "cluster_ipfsproxy"
|
|
|
|
)
|
2018-10-14 10:37:42 +00:00
|
|
|
|
|
|
|
// Default values for Config.
|
|
|
|
const (
|
2019-01-10 19:03:59 +00:00
|
|
|
DefaultListenAddr = "/ip4/127.0.0.1/tcp/9095"
|
|
|
|
DefaultNodeAddr = "/ip4/127.0.0.1/tcp/5001"
|
|
|
|
DefaultNodeHTTPS = false
|
|
|
|
DefaultReadTimeout = 0
|
|
|
|
DefaultReadHeaderTimeout = 5 * time.Second
|
|
|
|
DefaultWriteTimeout = 0
|
|
|
|
DefaultIdleTimeout = 60 * time.Second
|
|
|
|
DefaultExtractHeadersPath = "/api/v0/version"
|
2019-01-11 10:01:48 +00:00
|
|
|
DefaultExtractHeadersTTL = 5 * time.Minute
|
2018-10-14 10:37:42 +00:00
|
|
|
)
|
|
|
|
|
2018-10-14 12:30:19 +00:00
|
|
|
// Config allows to customize behaviour of IPFSProxy.
|
|
|
|
// It implements the config.ComponentConfig interface.
|
2018-10-13 14:27:03 +00:00
|
|
|
type Config struct {
|
|
|
|
config.Saver
|
|
|
|
|
2018-10-14 12:30:19 +00:00
|
|
|
// Listen parameters for the IPFS Proxy.
|
2018-12-12 18:51:21 +00:00
|
|
|
ListenAddr ma.Multiaddr
|
2018-10-13 14:27:03 +00:00
|
|
|
|
|
|
|
// Host/Port for the IPFS daemon.
|
|
|
|
NodeAddr ma.Multiaddr
|
|
|
|
|
2019-01-10 19:03:59 +00:00
|
|
|
// Should we talk to the IPFS API over HTTPS? (experimental, untested)
|
|
|
|
NodeHTTPS bool
|
|
|
|
|
2018-10-13 14:27:03 +00:00
|
|
|
// Maximum duration before timing out reading a full request
|
2018-12-12 18:51:21 +00:00
|
|
|
ReadTimeout time.Duration
|
2018-10-14 12:30:19 +00:00
|
|
|
|
2018-10-13 14:27:03 +00:00
|
|
|
// Maximum duration before timing out reading the headers of a request
|
2018-12-12 18:51:21 +00:00
|
|
|
ReadHeaderTimeout time.Duration
|
2018-10-13 14:27:03 +00:00
|
|
|
|
|
|
|
// Maximum duration before timing out write of the response
|
2018-12-12 18:51:21 +00:00
|
|
|
WriteTimeout time.Duration
|
2018-10-13 14:27:03 +00:00
|
|
|
|
|
|
|
// Server-side amount of time a Keep-Alive connection will be
|
|
|
|
// kept idle before being reused
|
2018-12-12 18:51:21 +00:00
|
|
|
IdleTimeout time.Duration
|
2019-01-10 19:03:59 +00:00
|
|
|
|
|
|
|
// A list of custom headers that should be extracted from
|
|
|
|
// IPFS daemon responses and re-used in responses from hijacked paths.
|
|
|
|
// This is only useful if the user has configured custom headers
|
|
|
|
// in the IFPS daemon. CORS-related headers are already
|
|
|
|
// taken care of by the proxy.
|
|
|
|
ExtractHeadersExtra []string
|
|
|
|
|
|
|
|
// If the user wants to extract some extra custom headers configured
|
|
|
|
// on the IPFS daemon so that they are used in hijacked responses,
|
|
|
|
// this request path will be used. Defaults to /version. This will
|
|
|
|
// trigger a single request to extract those headers and remember them
|
2019-01-11 10:01:48 +00:00
|
|
|
// for future requests (until TTL expires).
|
2019-01-10 19:03:59 +00:00
|
|
|
ExtractHeadersPath string
|
2019-01-11 10:01:48 +00:00
|
|
|
|
|
|
|
// Establishes how long we should remember extracted headers before we
|
|
|
|
// refresh them with a new request. 0 means always.
|
|
|
|
ExtractHeadersTTL time.Duration
|
2018-10-14 17:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type jsonConfig struct {
|
2018-12-12 18:51:21 +00:00
|
|
|
ListenMultiaddress string `json:"listen_multiaddress"`
|
2019-01-10 19:03:59 +00:00
|
|
|
NodeMultiaddress string `json:"node_multiaddress"`
|
|
|
|
NodeHTTPS string `json:"node_https,omitempty"`
|
|
|
|
|
|
|
|
ReadTimeout string `json:"read_timeout"`
|
|
|
|
ReadHeaderTimeout string `json:"read_header_timeout"`
|
|
|
|
WriteTimeout string `json:"write_timeout"`
|
|
|
|
IdleTimeout string `json:"idle_timeout"`
|
|
|
|
|
|
|
|
ExtractHeadersExtra []string `json:"extract_headers_extra,omitempty"`
|
|
|
|
ExtractHeadersPath string `json:"extract_headers_path,omitempty"`
|
2019-01-11 10:01:48 +00:00
|
|
|
ExtractHeadersTTL string `json:"extract_headers_ttl,omitempty"`
|
2018-12-17 02:54:17 +00:00
|
|
|
|
|
|
|
// Below fields are only here to maintain backward compatibility
|
|
|
|
// They will be removed in future
|
|
|
|
ProxyListenMultiaddress string `json:"proxy_listen_multiaddress,omitempty"`
|
|
|
|
ProxyReadTimeout string `json:"proxy_read_timeout,omitempty"`
|
|
|
|
ProxyReadHeaderTimeout string `json:"proxy_read_header_timeout,omitempty"`
|
|
|
|
ProxyWriteTimeout string `json:"proxy_write_timeout,omitempty"`
|
|
|
|
ProxyIdleTimeout string `json:"proxy_idle_timeout,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// toNewFields converts json config written in old style (fields starting with `proxy_`)
|
|
|
|
// to new style (without `proxy_`)
|
|
|
|
func (jcfg *jsonConfig) toNewFields() {
|
|
|
|
if jcfg.ListenMultiaddress == "" {
|
|
|
|
jcfg.ListenMultiaddress = jcfg.ProxyListenMultiaddress
|
|
|
|
}
|
|
|
|
|
|
|
|
if jcfg.ReadTimeout == "" {
|
|
|
|
jcfg.ReadTimeout = jcfg.ProxyReadTimeout
|
|
|
|
}
|
|
|
|
|
|
|
|
if jcfg.ReadHeaderTimeout == "" {
|
|
|
|
jcfg.ReadHeaderTimeout = jcfg.ProxyReadHeaderTimeout
|
|
|
|
}
|
|
|
|
|
|
|
|
if jcfg.WriteTimeout == "" {
|
|
|
|
jcfg.WriteTimeout = jcfg.ProxyWriteTimeout
|
|
|
|
}
|
|
|
|
|
|
|
|
if jcfg.IdleTimeout == "" {
|
|
|
|
jcfg.IdleTimeout = jcfg.ProxyIdleTimeout
|
|
|
|
}
|
2018-10-13 14:27:03 +00:00
|
|
|
}
|
|
|
|
|
2018-10-14 10:37:42 +00:00
|
|
|
// ConfigKey provides a human-friendly identifier for this type of Config.
|
|
|
|
func (cfg *Config) ConfigKey() string {
|
|
|
|
return configKey
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default sets the fields of this Config to sensible default values.
|
|
|
|
func (cfg *Config) Default() error {
|
2018-12-12 18:51:21 +00:00
|
|
|
proxy, err := ma.NewMultiaddr(DefaultListenAddr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
node, err := ma.NewMultiaddr(DefaultNodeAddr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cfg.ListenAddr = proxy
|
2018-10-14 10:37:42 +00:00
|
|
|
cfg.NodeAddr = node
|
2018-12-12 18:51:21 +00:00
|
|
|
cfg.ReadTimeout = DefaultReadTimeout
|
|
|
|
cfg.ReadHeaderTimeout = DefaultReadHeaderTimeout
|
|
|
|
cfg.WriteTimeout = DefaultWriteTimeout
|
|
|
|
cfg.IdleTimeout = DefaultIdleTimeout
|
2019-01-10 19:03:59 +00:00
|
|
|
cfg.ExtractHeadersExtra = nil
|
|
|
|
cfg.ExtractHeadersPath = DefaultExtractHeadersPath
|
2019-01-11 10:01:48 +00:00
|
|
|
cfg.ExtractHeadersTTL = DefaultExtractHeadersTTL
|
2018-10-14 10:37:42 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-10-13 14:27:03 +00:00
|
|
|
// Validate checks that the fields of this Config have sensible values,
|
|
|
|
// at least in appearance.
|
|
|
|
func (cfg *Config) Validate() error {
|
|
|
|
var err error
|
2018-12-12 18:51:21 +00:00
|
|
|
if cfg.ListenAddr == nil {
|
|
|
|
err = errors.New("ipfsproxy.listen_multiaddress not set")
|
2018-10-13 14:27:03 +00:00
|
|
|
}
|
|
|
|
if cfg.NodeAddr == nil {
|
2018-11-11 15:39:13 +00:00
|
|
|
err = errors.New("ipfsproxy.node_multiaddress not set")
|
2018-10-13 14:27:03 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 18:51:21 +00:00
|
|
|
if cfg.ReadTimeout < 0 {
|
|
|
|
err = errors.New("ipfsproxy.read_timeout is invalid")
|
2018-10-13 14:27:03 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 18:51:21 +00:00
|
|
|
if cfg.ReadHeaderTimeout < 0 {
|
|
|
|
err = errors.New("ipfsproxy.read_header_timeout is invalid")
|
2018-10-13 14:27:03 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 18:51:21 +00:00
|
|
|
if cfg.WriteTimeout < 0 {
|
|
|
|
err = errors.New("ipfsproxy.write_timeout is invalid")
|
2018-10-13 14:27:03 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 18:51:21 +00:00
|
|
|
if cfg.IdleTimeout < 0 {
|
|
|
|
err = errors.New("ipfsproxy.idle_timeout invalid")
|
2018-10-13 14:27:03 +00:00
|
|
|
}
|
|
|
|
|
2019-01-10 19:03:59 +00:00
|
|
|
if cfg.ExtractHeadersPath == "" {
|
|
|
|
err = errors.New("ipfsproxy.extract_headers_path should not be empty")
|
|
|
|
}
|
2018-10-14 10:37:42 +00:00
|
|
|
|
2019-01-11 10:01:48 +00:00
|
|
|
if cfg.ExtractHeadersTTL < 0 {
|
|
|
|
err = errors.New("ipfsproxy.extract_headers_ttl is invalid")
|
|
|
|
}
|
|
|
|
|
2019-01-10 19:03:59 +00:00
|
|
|
return err
|
2018-10-14 10:37:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LoadJSON parses a JSON representation of this Config as generated by ToJSON.
|
|
|
|
func (cfg *Config) LoadJSON(raw []byte) error {
|
|
|
|
jcfg := &jsonConfig{}
|
|
|
|
err := json.Unmarshal(raw, jcfg)
|
|
|
|
if err != nil {
|
2018-11-11 10:53:36 +00:00
|
|
|
logger.Error("Error unmarshaling ipfsproxy config")
|
2018-10-14 10:37:42 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-12-17 02:54:17 +00:00
|
|
|
// This is here only here to maintain backward compatibility
|
|
|
|
// This won't be needed after removing old style fields(starting with `proxy_`)
|
|
|
|
jcfg.toNewFields()
|
2018-12-12 18:51:21 +00:00
|
|
|
|
|
|
|
err = cfg.Default()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error setting config to default values: %s", err)
|
|
|
|
}
|
2018-10-14 10:37:42 +00:00
|
|
|
|
2018-12-17 02:54:17 +00:00
|
|
|
// override json config with env var
|
|
|
|
err = envconfig.Process("cluster_ipfsproxy", jcfg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-12-12 18:51:21 +00:00
|
|
|
proxyAddr, err := ma.NewMultiaddr(jcfg.ListenMultiaddress)
|
2018-10-14 10:37:42 +00:00
|
|
|
if err != nil {
|
2018-12-12 18:51:21 +00:00
|
|
|
return fmt.Errorf("error parsing proxy listen_multiaddress: %s", err)
|
2018-10-14 10:37:42 +00:00
|
|
|
}
|
|
|
|
nodeAddr, err := ma.NewMultiaddr(jcfg.NodeMultiaddress)
|
|
|
|
if err != nil {
|
2018-12-12 18:51:21 +00:00
|
|
|
return fmt.Errorf("error parsing ipfs node_multiaddress: %s", err)
|
2018-10-14 10:37:42 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 18:51:21 +00:00
|
|
|
cfg.ListenAddr = proxyAddr
|
2018-10-14 10:37:42 +00:00
|
|
|
cfg.NodeAddr = nodeAddr
|
2019-01-10 19:03:59 +00:00
|
|
|
config.SetIfNotDefault(jcfg.NodeHTTPS, &cfg.NodeHTTPS)
|
2018-10-14 10:37:42 +00:00
|
|
|
|
|
|
|
err = config.ParseDurations(
|
2018-11-12 00:03:07 +00:00
|
|
|
"ipfsproxy",
|
2018-12-12 18:51:21 +00:00
|
|
|
&config.DurationOpt{Duration: jcfg.ReadTimeout, Dst: &cfg.ReadTimeout, Name: "read_timeout"},
|
|
|
|
&config.DurationOpt{Duration: jcfg.ReadHeaderTimeout, Dst: &cfg.ReadHeaderTimeout, Name: "read_header_timeout"},
|
|
|
|
&config.DurationOpt{Duration: jcfg.WriteTimeout, Dst: &cfg.WriteTimeout, Name: "write_timeout"},
|
|
|
|
&config.DurationOpt{Duration: jcfg.IdleTimeout, Dst: &cfg.IdleTimeout, Name: "idle_timeout"},
|
2019-01-11 10:01:48 +00:00
|
|
|
&config.DurationOpt{Duration: jcfg.ExtractHeadersTTL, Dst: &cfg.ExtractHeadersTTL, Name: "extract_header_ttl"},
|
2018-10-14 10:37:42 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-10 19:03:59 +00:00
|
|
|
if extra := jcfg.ExtractHeadersExtra; extra != nil && len(extra) > 0 {
|
|
|
|
cfg.ExtractHeadersExtra = extra
|
|
|
|
}
|
|
|
|
config.SetIfNotDefault(jcfg.ExtractHeadersPath, &cfg.ExtractHeadersPath)
|
|
|
|
|
2018-10-14 10:37:42 +00:00
|
|
|
return cfg.Validate()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToJSON generates a human-friendly JSON representation of this Config.
|
|
|
|
func (cfg *Config) ToJSON() (raw []byte, err error) {
|
|
|
|
// Multiaddress String() may panic
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
err = fmt.Errorf("%s", r)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
jcfg := &jsonConfig{}
|
|
|
|
|
|
|
|
// Set all configuration fields
|
2018-12-12 18:51:21 +00:00
|
|
|
jcfg.ListenMultiaddress = cfg.ListenAddr.String()
|
2018-10-14 10:37:42 +00:00
|
|
|
jcfg.NodeMultiaddress = cfg.NodeAddr.String()
|
2018-12-12 18:51:21 +00:00
|
|
|
jcfg.ReadTimeout = cfg.ReadTimeout.String()
|
|
|
|
jcfg.ReadHeaderTimeout = cfg.ReadHeaderTimeout.String()
|
|
|
|
jcfg.WriteTimeout = cfg.WriteTimeout.String()
|
|
|
|
jcfg.IdleTimeout = cfg.IdleTimeout.String()
|
2018-10-14 10:37:42 +00:00
|
|
|
|
2019-01-11 10:01:48 +00:00
|
|
|
jcfg.ExtractHeadersExtra = cfg.ExtractHeadersExtra
|
|
|
|
jcfg.ExtractHeadersPath = cfg.ExtractHeadersPath
|
|
|
|
jcfg.ExtractHeadersTTL = cfg.ExtractHeadersTTL.String()
|
|
|
|
|
2018-10-14 10:37:42 +00:00
|
|
|
raw, err = config.DefaultJSONMarshal(jcfg)
|
|
|
|
return
|
2018-10-13 14:27:03 +00:00
|
|
|
}
|