Issue #453 Extract the IPFS Proxy from ipfshttp

Changes as requieed
Rename IPFSProxy struct to Server

License: MIT
Signed-off-by: Kishan Mohanbhai Sagathiya <kishansagathiya@gmail.com>
This commit is contained in:
Kishan Sagathiya 2018-11-01 15:54:05 +05:30
parent fdb573c96f
commit 3a5ad6111a
5 changed files with 78 additions and 87 deletions

View File

@ -21,7 +21,6 @@ const (
DefaultProxyReadHeaderTimeout = 5 * time.Second DefaultProxyReadHeaderTimeout = 5 * time.Second
DefaultProxyWriteTimeout = 0 DefaultProxyWriteTimeout = 0
DefaultProxyIdleTimeout = 60 * time.Second DefaultProxyIdleTimeout = 60 * time.Second
DefaultIPFSRequestTimeout = 5 * time.Minute
) )
// Config allows to customize behaviour of IPFSProxy. // Config allows to customize behaviour of IPFSProxy.
@ -47,9 +46,6 @@ type Config struct {
// Server-side amount of time a Keep-Alive connection will be // Server-side amount of time a Keep-Alive connection will be
// kept idle before being reused // kept idle before being reused
ProxyIdleTimeout time.Duration ProxyIdleTimeout time.Duration
// IPFS Daemon HTTP Client POST timeout
IPFSRequestTimeout time.Duration
} }
type jsonConfig struct { type jsonConfig struct {
@ -59,7 +55,6 @@ type jsonConfig struct {
ProxyReadHeaderTimeout string `json:"proxy_read_header_timeout"` ProxyReadHeaderTimeout string `json:"proxy_read_header_timeout"`
ProxyWriteTimeout string `json:"proxy_write_timeout"` ProxyWriteTimeout string `json:"proxy_write_timeout"`
ProxyIdleTimeout string `json:"proxy_idle_timeout"` ProxyIdleTimeout string `json:"proxy_idle_timeout"`
IPFSRequestTimeout string `json:"ipfs_request_timeout"`
} }
// ConfigKey provides a human-friendly identifier for this type of Config. // ConfigKey provides a human-friendly identifier for this type of Config.
@ -77,7 +72,6 @@ func (cfg *Config) Default() error {
cfg.ProxyReadHeaderTimeout = DefaultProxyReadHeaderTimeout cfg.ProxyReadHeaderTimeout = DefaultProxyReadHeaderTimeout
cfg.ProxyWriteTimeout = DefaultProxyWriteTimeout cfg.ProxyWriteTimeout = DefaultProxyWriteTimeout
cfg.ProxyIdleTimeout = DefaultProxyIdleTimeout cfg.ProxyIdleTimeout = DefaultProxyIdleTimeout
cfg.IPFSRequestTimeout = DefaultIPFSRequestTimeout
return nil return nil
} }
@ -109,10 +103,6 @@ func (cfg *Config) Validate() error {
err = errors.New("ipfshttp.proxy_idle_timeout invalid") err = errors.New("ipfshttp.proxy_idle_timeout invalid")
} }
if cfg.IPFSRequestTimeout < 0 {
err = errors.New("ipfshttp.ipfs_request_timeout invalid")
}
return err return err
} }
@ -146,7 +136,6 @@ func (cfg *Config) LoadJSON(raw []byte) error {
&config.DurationOpt{Duration: jcfg.ProxyReadHeaderTimeout, Dst: &cfg.ProxyReadHeaderTimeout, Name: "proxy_read_header_timeout"}, &config.DurationOpt{Duration: jcfg.ProxyReadHeaderTimeout, Dst: &cfg.ProxyReadHeaderTimeout, Name: "proxy_read_header_timeout"},
&config.DurationOpt{Duration: jcfg.ProxyWriteTimeout, Dst: &cfg.ProxyWriteTimeout, Name: "proxy_write_timeout"}, &config.DurationOpt{Duration: jcfg.ProxyWriteTimeout, Dst: &cfg.ProxyWriteTimeout, Name: "proxy_write_timeout"},
&config.DurationOpt{Duration: jcfg.ProxyIdleTimeout, Dst: &cfg.ProxyIdleTimeout, Name: "proxy_idle_timeout"}, &config.DurationOpt{Duration: jcfg.ProxyIdleTimeout, Dst: &cfg.ProxyIdleTimeout, Name: "proxy_idle_timeout"},
&config.DurationOpt{Duration: jcfg.IPFSRequestTimeout, Dst: &cfg.IPFSRequestTimeout, Name: "ipfs_request_timeout"},
) )
if err != nil { if err != nil {
return err return err
@ -173,7 +162,6 @@ func (cfg *Config) ToJSON() (raw []byte, err error) {
jcfg.ProxyReadHeaderTimeout = cfg.ProxyReadHeaderTimeout.String() jcfg.ProxyReadHeaderTimeout = cfg.ProxyReadHeaderTimeout.String()
jcfg.ProxyWriteTimeout = cfg.ProxyWriteTimeout.String() jcfg.ProxyWriteTimeout = cfg.ProxyWriteTimeout.String()
jcfg.ProxyIdleTimeout = cfg.ProxyIdleTimeout.String() jcfg.ProxyIdleTimeout = cfg.ProxyIdleTimeout.String()
jcfg.IPFSRequestTimeout = cfg.IPFSRequestTimeout.String()
raw, err = config.DefaultJSONMarshal(jcfg) raw, err = config.DefaultJSONMarshal(jcfg)
return return

View File

@ -30,12 +30,12 @@ var DNSTimeout = 5 * time.Second
var logger = logging.Logger("ipfsproxy") var logger = logging.Logger("ipfsproxy")
// IPFSProxy offers an IPFS API, hijacking some interesting requests // Server offers an IPFS API, hijacking some interesting requests
// and forwarding the rest to the ipfs daemon // and forwarding the rest to the ipfs daemon
// it proxies HTTP requests to the configured IPFS // it proxies HTTP requests to the configured IPFS
// daemon. It is able to intercept these requests though, and // daemon. It is able to intercept these requests though, and
// perform extra operations on them. // perform extra operations on them.
type IPFSProxy struct { type Server struct {
ctx context.Context ctx context.Context
cancel func() cancel func()
@ -77,8 +77,8 @@ type ipfsAddResp struct {
Size string `json:",omitempty"` Size string `json:",omitempty"`
} }
// NewIPFSProxy returns and IPFSProxy component // New returns and ipfs Proxy component
func NewIPFSProxy(cfg *Config) (*IPFSProxy, error) { func New(cfg *Config) (*Server, error) {
err := cfg.Validate() err := cfg.Validate()
if err != nil { if err != nil {
return nil, err return nil, err
@ -132,11 +132,11 @@ func NewIPFSProxy(cfg *Config) (*IPFSProxy, error) {
// See: https://github.com/ipfs/go-ipfs/issues/5168 // See: https://github.com/ipfs/go-ipfs/issues/5168
// See: https://github.com/ipfs/ipfs-cluster/issues/548 // See: https://github.com/ipfs/ipfs-cluster/issues/548
// on why this is re-enabled. // on why this is re-enabled.
s.SetKeepAlivesEnabled(false) // A reminder that this can be changed s.SetKeepAlivesEnabled(true) // A reminder that this can be changed
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
ipfs := &IPFSProxy{ proxy := &Server{
ctx: ctx, ctx: ctx,
config: cfg, config: cfg,
cancel: cancel, cancel: cancel,
@ -146,70 +146,68 @@ func NewIPFSProxy(cfg *Config) (*IPFSProxy, error) {
server: s, server: s,
} }
smux.Handle("/", proxyHandler) smux.Handle("/", proxyHandler)
smux.HandleFunc("/api/v0/pin/add", ipfs.pinHandler) smux.HandleFunc("/api/v0/pin/add/", proxy.pinHandler)
smux.HandleFunc("/api/v0/pin/add/", ipfs.pinHandler) smux.HandleFunc("/api/v0/pin/rm/", proxy.unpinHandler)
smux.HandleFunc("/api/v0/pin/rm", ipfs.unpinHandler) smux.HandleFunc("/api/v0/pin/ls", proxy.pinLsHandler) // required to handle /pin/ls for all pins
smux.HandleFunc("/api/v0/pin/rm/", ipfs.unpinHandler) smux.HandleFunc("/api/v0/pin/ls/", proxy.pinLsHandler)
smux.HandleFunc("/api/v0/pin/ls", ipfs.pinLsHandler) // required to handle /pin/ls for all pins smux.HandleFunc("/api/v0/add", proxy.addHandler)
smux.HandleFunc("/api/v0/pin/ls/", ipfs.pinLsHandler) smux.HandleFunc("/api/v0/add/", proxy.addHandler)
smux.HandleFunc("/api/v0/add", ipfs.addHandler) smux.HandleFunc("/api/v0/repo/stat", proxy.repoStatHandler)
smux.HandleFunc("/api/v0/add/", ipfs.addHandler) smux.HandleFunc("/api/v0/repo/stat/", proxy.repoStatHandler)
smux.HandleFunc("/api/v0/repo/stat", ipfs.repoStatHandler)
smux.HandleFunc("/api/v0/repo/stat/", ipfs.repoStatHandler)
go ipfs.run() go proxy.run()
return ipfs, nil return proxy, nil
} }
// SetClient makes the component ready to perform RPC // SetClient makes the component ready to perform RPC
// requests. // requests.
func (ipfs *IPFSProxy) SetClient(c *rpc.Client) { func (proxy *Server) SetClient(c *rpc.Client) {
ipfs.rpcClient = c proxy.rpcClient = c
ipfs.rpcReady <- struct{}{} proxy.rpcReady <- struct{}{}
} }
// Shutdown stops any listeners and stops the component from taking // Shutdown stops any listeners and stops the component from taking
// any requests. // any requests.
func (ipfs *IPFSProxy) Shutdown() error { func (proxy *Server) Shutdown() error {
ipfs.shutdownLock.Lock() proxy.shutdownLock.Lock()
defer ipfs.shutdownLock.Unlock() defer proxy.shutdownLock.Unlock()
if ipfs.shutdown { if proxy.shutdown {
logger.Debug("already shutdown") logger.Debug("already shutdown")
return nil return nil
} }
logger.Info("stopping IPFS Proxy") logger.Info("stopping IPFS Proxy")
ipfs.cancel() proxy.cancel()
close(ipfs.rpcReady) close(proxy.rpcReady)
ipfs.server.SetKeepAlivesEnabled(false) proxy.server.SetKeepAlivesEnabled(false)
ipfs.listener.Close() proxy.listener.Close()
ipfs.wg.Wait() proxy.wg.Wait()
ipfs.shutdown = true proxy.shutdown = true
return nil return nil
} }
// launches proxy when we receive the rpcReady signal. // launches proxy when we receive the rpcReady signal.
func (ipfs *IPFSProxy) run() { func (proxy *Server) run() {
<-ipfs.rpcReady <-proxy.rpcReady
// Do not shutdown while launching threads // Do not shutdown while launching threads
// -- prevents race conditions with ipfs.wg. // -- prevents race conditions with proxy.wg.
ipfs.shutdownLock.Lock() proxy.shutdownLock.Lock()
defer ipfs.shutdownLock.Unlock() defer proxy.shutdownLock.Unlock()
// This launches the proxy // This launches the proxy
ipfs.wg.Add(1) proxy.wg.Add(1)
go func() { go func() {
defer ipfs.wg.Done() defer proxy.wg.Done()
logger.Infof( logger.Infof(
"IPFS Proxy: %s -> %s", "IPFS Proxy: %s -> %s",
ipfs.config.ProxyAddr, proxy.config.ProxyAddr,
ipfs.config.NodeAddr, proxy.config.NodeAddr,
) )
err := ipfs.server.Serve(ipfs.listener) // hangs here err := proxy.server.Serve(proxy.listener) // hangs here
if err != nil && !strings.Contains(err.Error(), "closed network connection") { if err != nil && !strings.Contains(err.Error(), "closed network connection") {
logger.Error(err) logger.Error(err)
} }
@ -226,7 +224,7 @@ func ipfsErrorResponder(w http.ResponseWriter, errMsg string) {
return return
} }
func (ipfs *IPFSProxy) pinOpHandler(op string, w http.ResponseWriter, r *http.Request) { func (proxy *Server) pinOpHandler(op string, w http.ResponseWriter, r *http.Request) {
arg, ok := extractArgument(r.URL) arg, ok := extractArgument(r.URL)
if !ok { if !ok {
ipfsErrorResponder(w, "Error: bad argument") ipfsErrorResponder(w, "Error: bad argument")
@ -238,7 +236,7 @@ func (ipfs *IPFSProxy) pinOpHandler(op string, w http.ResponseWriter, r *http.Re
return return
} }
err = ipfs.rpcClient.Call( err = proxy.rpcClient.Call(
"", "",
"Cluster", "Cluster",
op, op,
@ -260,15 +258,15 @@ func (ipfs *IPFSProxy) pinOpHandler(op string, w http.ResponseWriter, r *http.Re
return return
} }
func (ipfs *IPFSProxy) pinHandler(w http.ResponseWriter, r *http.Request) { func (proxy *Server) pinHandler(w http.ResponseWriter, r *http.Request) {
ipfs.pinOpHandler("Pin", w, r) proxy.pinOpHandler("Pin", w, r)
} }
func (ipfs *IPFSProxy) unpinHandler(w http.ResponseWriter, r *http.Request) { func (proxy *Server) unpinHandler(w http.ResponseWriter, r *http.Request) {
ipfs.pinOpHandler("Unpin", w, r) proxy.pinOpHandler("Unpin", w, r)
} }
func (ipfs *IPFSProxy) pinLsHandler(w http.ResponseWriter, r *http.Request) { func (proxy *Server) pinLsHandler(w http.ResponseWriter, r *http.Request) {
pinLs := ipfsPinLsResp{} pinLs := ipfsPinLsResp{}
pinLs.Keys = make(map[string]ipfsPinType) pinLs.Keys = make(map[string]ipfsPinType)
@ -280,7 +278,7 @@ func (ipfs *IPFSProxy) pinLsHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
var pin api.PinSerial var pin api.PinSerial
err = ipfs.rpcClient.Call( err = proxy.rpcClient.Call(
"", "",
"Cluster", "Cluster",
"PinGet", "PinGet",
@ -296,7 +294,7 @@ func (ipfs *IPFSProxy) pinLsHandler(w http.ResponseWriter, r *http.Request) {
} }
} else { } else {
var pins []api.PinSerial var pins []api.PinSerial
err := ipfs.rpcClient.Call( err := proxy.rpcClient.Call(
"", "",
"Cluster", "Cluster",
"Pins", "Pins",
@ -321,7 +319,7 @@ func (ipfs *IPFSProxy) pinLsHandler(w http.ResponseWriter, r *http.Request) {
w.Write(resBytes) w.Write(resBytes)
} }
func (ipfs *IPFSProxy) addHandler(w http.ResponseWriter, r *http.Request) { func (proxy *Server) addHandler(w http.ResponseWriter, r *http.Request) {
reader, err := r.MultipartReader() reader, err := r.MultipartReader()
if err != nil { if err != nil {
ipfsErrorResponder(w, "error reading request: "+err.Error()) ipfsErrorResponder(w, "error reading request: "+err.Error())
@ -362,8 +360,8 @@ func (ipfs *IPFSProxy) addHandler(w http.ResponseWriter, r *http.Request) {
} }
root, err := adderutils.AddMultipartHTTPHandler( root, err := adderutils.AddMultipartHTTPHandler(
ipfs.ctx, proxy.ctx,
ipfs.rpcClient, proxy.rpcClient,
params, params,
reader, reader,
w, w,
@ -381,8 +379,8 @@ func (ipfs *IPFSProxy) addHandler(w http.ResponseWriter, r *http.Request) {
// Unpin because the user doesn't want to pin // Unpin because the user doesn't want to pin
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
err = ipfs.rpcClient.CallContext( err = proxy.rpcClient.CallContext(
ipfs.ctx, proxy.ctx,
"", "",
"Cluster", "Cluster",
"Unpin", "Unpin",
@ -395,9 +393,9 @@ func (ipfs *IPFSProxy) addHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
func (ipfs *IPFSProxy) repoStatHandler(w http.ResponseWriter, r *http.Request) { func (proxy *Server) repoStatHandler(w http.ResponseWriter, r *http.Request) {
var peers []peer.ID var peers []peer.ID
err := ipfs.rpcClient.Call( err := proxy.rpcClient.Call(
"", "",
"Cluster", "Cluster",
"ConsensusPeers", "ConsensusPeers",
@ -409,7 +407,7 @@ func (ipfs *IPFSProxy) repoStatHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
ctxs, cancels := rpcutil.CtxsWithTimeout(ipfs.ctx, len(peers), ipfs.config.IPFSRequestTimeout) ctxs, cancels := rpcutil.CtxsWithCancel(proxy.ctx, len(peers))
defer rpcutil.MultiCancel(cancels) defer rpcutil.MultiCancel(cancels)
repoStats := make([]api.IPFSRepoStat, len(peers), len(peers)) repoStats := make([]api.IPFSRepoStat, len(peers), len(peers))
@ -418,7 +416,7 @@ func (ipfs *IPFSProxy) repoStatHandler(w http.ResponseWriter, r *http.Request) {
repoStatsIfaces[i] = &repoStats[i] repoStatsIfaces[i] = &repoStats[i]
} }
errs := ipfs.rpcClient.MultiCall( errs := proxy.rpcClient.MultiCall(
ctxs, ctxs,
peers, peers,
"Cluster", "Cluster",

View File

@ -48,8 +48,7 @@ type Cluster struct {
peerManager *pstoremgr.Manager peerManager *pstoremgr.Manager
consensus Consensus consensus Consensus
api API apis []API
proxy API
ipfs IPFSConnector ipfs IPFSConnector
state state.State state state.State
tracker PinTracker tracker PinTracker
@ -78,8 +77,7 @@ func NewCluster(
host host.Host, host host.Host,
cfg *Config, cfg *Config,
consensus Consensus, consensus Consensus,
api API, apis []API,
proxy API,
ipfs IPFSConnector, ipfs IPFSConnector,
st state.State, st state.State,
tracker PinTracker, tracker PinTracker,
@ -132,8 +130,7 @@ func NewCluster(
host: rHost, host: rHost,
dht: idht, dht: idht,
consensus: consensus, consensus: consensus,
api: api, apis: apis,
proxy: proxy,
ipfs: ipfs, ipfs: ipfs,
state: st, state: st,
tracker: tracker, tracker: tracker,
@ -176,8 +173,9 @@ func (c *Cluster) setupRPC() error {
func (c *Cluster) setupRPCClients() { func (c *Cluster) setupRPCClients() {
c.tracker.SetClient(c.rpcClient) c.tracker.SetClient(c.rpcClient)
c.ipfs.SetClient(c.rpcClient) c.ipfs.SetClient(c.rpcClient)
c.api.SetClient(c.rpcClient) for _, api := range c.apis {
c.proxy.SetClient(c.rpcClient) api.SetClient(c.rpcClient)
}
c.consensus.SetClient(c.rpcClient) c.consensus.SetClient(c.rpcClient)
c.monitor.SetClient(c.rpcClient) c.monitor.SetClient(c.rpcClient)
c.allocator.SetClient(c.rpcClient) c.allocator.SetClient(c.rpcClient)
@ -475,10 +473,13 @@ func (c *Cluster) Shutdown() error {
return err return err
} }
if err := c.api.Shutdown(); err != nil { for _, api := range c.apis {
if err := api.Shutdown(); err != nil {
logger.Errorf("error stopping API: %s", err) logger.Errorf("error stopping API: %s", err)
return err return err
} }
}
if err := c.ipfs.Shutdown(); err != nil { if err := c.ipfs.Shutdown(); err != nil {
logger.Errorf("error stopping IPFS Connector: %s", err) logger.Errorf("error stopping IPFS Connector: %s", err)
return err return err

View File

@ -111,12 +111,14 @@ func createCluster(
api, err := rest.NewAPIWithHost(cfgs.apiCfg, host) api, err := rest.NewAPIWithHost(cfgs.apiCfg, host)
checkErr("creating REST API component", err) checkErr("creating REST API component", err)
proxy, err := ipfsproxy.New(cfgs.ipfsproxyCfg)
checkErr("creating IPFS Proxy component", err)
apis := []ipfscluster.API{api, proxy}
connector, err := ipfshttp.NewConnector(cfgs.ipfshttpCfg) connector, err := ipfshttp.NewConnector(cfgs.ipfshttpCfg)
checkErr("creating IPFS Connector component", err) checkErr("creating IPFS Connector component", err)
proxy, err := ipfsproxy.NewIPFSProxy(cfgs.ipfsproxyCfg)
checkErr("creating IPFS Proxy component", err)
state := mapstate.NewMapState() state := mapstate.NewMapState()
err = validateVersion(cfgs.clusterCfg, cfgs.consensusCfg) err = validateVersion(cfgs.clusterCfg, cfgs.consensusCfg)
@ -140,8 +142,7 @@ func createCluster(
host, host,
cfgs.clusterCfg, cfgs.clusterCfg,
raftcon, raftcon,
api, apis,
proxy,
connector, connector,
state, state,
tracker, tracker,

View File

@ -186,6 +186,9 @@ func (ipfs *Connector) Shutdown() error {
ipfs.wg.Wait() ipfs.wg.Wait()
ipfs.shutdown = true ipfs.shutdown = true
logger.Info("stopping IPFS Connector")
return nil return nil
} }