From 0ccfb889bab33b95526ccb2d2b6b89ad67ace838 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 31 Jul 2015 17:34:56 -0400 Subject: [PATCH 1/2] corehttp: add net.Listener to ServeOption ServeOptions take the node and muxer, they should get the listener too as sometimes they need to operate on the listener address. License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/kubo@3f1cbe2f437c9aad8b972b40283d2dc69a2bd6b9 --- gateway/core/corehttp/commands.go | 3 ++- gateway/core/corehttp/corehttp.go | 8 ++++---- gateway/core/corehttp/gateway.go | 5 +++-- gateway/core/corehttp/gateway_test.go | 20 ++++++++++++++++---- gateway/core/corehttp/ipns_hostname.go | 3 ++- gateway/core/corehttp/logs.go | 3 ++- gateway/core/corehttp/prometheus.go | 3 ++- gateway/core/corehttp/redirect.go | 3 ++- 8 files changed, 33 insertions(+), 15 deletions(-) diff --git a/gateway/core/corehttp/commands.go b/gateway/core/corehttp/commands.go index 0a65fcc3a..1793c5539 100644 --- a/gateway/core/corehttp/commands.go +++ b/gateway/core/corehttp/commands.go @@ -1,6 +1,7 @@ package corehttp import ( + "net" "net/http" "os" "strings" @@ -58,7 +59,7 @@ func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) { } func CommandsOption(cctx commands.Context) ServeOption { - return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { + return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { cfg := &cmdsHttp.ServerConfig{ CORSOpts: &cors.Options{ diff --git a/gateway/core/corehttp/corehttp.go b/gateway/core/corehttp/corehttp.go index 042f056ad..dc221f3cd 100644 --- a/gateway/core/corehttp/corehttp.go +++ b/gateway/core/corehttp/corehttp.go @@ -23,16 +23,16 @@ var log = eventlog.Logger("core/server") // It returns the mux to expose to future options, which may be a new mux if it // is interested in mediating requests to future options, or the same mux // initially passed in if not. -type ServeOption func(*core.IpfsNode, *http.ServeMux) (*http.ServeMux, error) +type ServeOption func(*core.IpfsNode, net.Listener, *http.ServeMux) (*http.ServeMux, error) // makeHandler turns a list of ServeOptions into a http.Handler that implements // all of the given options, in order. -func makeHandler(n *core.IpfsNode, options ...ServeOption) (http.Handler, error) { +func makeHandler(n *core.IpfsNode, l net.Listener, options ...ServeOption) (http.Handler, error) { topMux := http.NewServeMux() mux := topMux for _, option := range options { var err error - mux, err = option(n, mux) + mux, err = option(n, l, mux) if err != nil { return nil, err } @@ -65,7 +65,7 @@ func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...Serv } func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error { - handler, err := makeHandler(node, options...) + handler, err := makeHandler(node, lis, options...) if err != nil { return err } diff --git a/gateway/core/corehttp/gateway.go b/gateway/core/corehttp/gateway.go index f70a1d11f..584a89437 100644 --- a/gateway/core/corehttp/gateway.go +++ b/gateway/core/corehttp/gateway.go @@ -2,6 +2,7 @@ package corehttp import ( "fmt" + "net" "net/http" "sync" @@ -27,7 +28,7 @@ func NewGateway(conf GatewayConfig) *Gateway { } func (g *Gateway) ServeOption() ServeOption { - return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { // pass user's HTTP headers g.Config.Headers = n.Repo.Config().Gateway.HTTPHeaders @@ -50,7 +51,7 @@ func GatewayOption(writable bool) ServeOption { } func VersionOption() ServeOption { - return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Client Version: %s\n", id.ClientVersion) fmt.Fprintf(w, "Protocol Version: %s\n", id.IpfsVersion) diff --git a/gateway/core/corehttp/gateway_test.go b/gateway/core/corehttp/gateway_test.go index 01d4295b7..7ad3584da 100644 --- a/gateway/core/corehttp/gateway_test.go +++ b/gateway/core/corehttp/gateway_test.go @@ -55,6 +55,14 @@ func newNodeWithMockNamesys(t *testing.T, ns mockNamesys) *core.IpfsNode { return n } +type delegatedHandler struct { + http.Handler +} + +func (dh *delegatedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + dh.Handler.ServeHTTP(w, r) +} + func TestGatewayGet(t *testing.T) { t.Skip("not sure whats going on here") ns := mockNamesys{} @@ -65,7 +73,14 @@ func TestGatewayGet(t *testing.T) { } ns["example.com"] = path.FromString("/ipfs/" + k) - h, err := makeHandler(n, + // need this variable here since we need to construct handler with + // listener, and server with handler. yay cycles. + dh := &delegatedHandler{} + ts := httptest.NewServer(dh) + defer ts.Close() + + dh.Handler, err = makeHandler(n, + ts.Listener, IPNSHostnameOption(), GatewayOption(false), ) @@ -73,9 +88,6 @@ func TestGatewayGet(t *testing.T) { t.Fatal(err) } - ts := httptest.NewServer(h) - defer ts.Close() - t.Log(ts.URL) for _, test := range []struct { host string diff --git a/gateway/core/corehttp/ipns_hostname.go b/gateway/core/corehttp/ipns_hostname.go index 6f31e5268..10edb0ace 100644 --- a/gateway/core/corehttp/ipns_hostname.go +++ b/gateway/core/corehttp/ipns_hostname.go @@ -1,6 +1,7 @@ package corehttp import ( + "net" "net/http" "strings" @@ -13,7 +14,7 @@ import ( // an IPNS name. // The rewritten request points at the resolved name on the gateway handler. func IPNSHostnameOption() ServeOption { - return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { childMux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithCancel(n.Context()) diff --git a/gateway/core/corehttp/logs.go b/gateway/core/corehttp/logs.go index 7624644cf..59b87b9bc 100644 --- a/gateway/core/corehttp/logs.go +++ b/gateway/core/corehttp/logs.go @@ -2,6 +2,7 @@ package corehttp import ( "io" + "net" "net/http" core "github.com/ipfs/go-ipfs/core" @@ -36,7 +37,7 @@ func (w *writeErrNotifier) Write(b []byte) (int, error) { } func LogOption() ServeOption { - return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { mux.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) wnf, errs := newWriteErrNotifier(w) diff --git a/gateway/core/corehttp/prometheus.go b/gateway/core/corehttp/prometheus.go index d6e8ef4d0..0642c04b5 100644 --- a/gateway/core/corehttp/prometheus.go +++ b/gateway/core/corehttp/prometheus.go @@ -1,6 +1,7 @@ package corehttp import ( + "net" "net/http" prom "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus" @@ -9,7 +10,7 @@ import ( ) func PrometheusOption(path string) ServeOption { - return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { mux.Handle(path, prom.Handler()) return mux, nil } diff --git a/gateway/core/corehttp/redirect.go b/gateway/core/corehttp/redirect.go index 67d6c0773..ec70ffaf9 100644 --- a/gateway/core/corehttp/redirect.go +++ b/gateway/core/corehttp/redirect.go @@ -1,6 +1,7 @@ package corehttp import ( + "net" "net/http" core "github.com/ipfs/go-ipfs/core" @@ -8,7 +9,7 @@ import ( func RedirectOption(path string, redirect string) ServeOption { handler := &redirectHandler{redirect} - return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { mux.Handle("/"+path+"/", handler) return mux, nil } From 5d8247040edfe0f6e2f931f65b415e189c5a2b5c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 31 Jul 2015 17:36:02 -0400 Subject: [PATCH 2/2] fix cors: defaults should take the port of the listener need to do it this way to avoid VERY confusing situations where the user would change the API port (to another port, or maybe even to :0). this way things dont break on the user, and by default, users only need to change the API address and things should still "just work" License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/kubo@3ee83a7c5eae50496a9a349330bc70e4d635393c --- gateway/core/corehttp/commands.go | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/gateway/core/corehttp/commands.go b/gateway/core/corehttp/commands.go index 1793c5539..d96818e2a 100644 --- a/gateway/core/corehttp/commands.go +++ b/gateway/core/corehttp/commands.go @@ -4,6 +4,7 @@ import ( "net" "net/http" "os" + "strconv" "strings" cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors" @@ -29,6 +30,13 @@ or ipfs daemon --api-http-header 'Access-Control-Allow-Origin: *' ` +var defaultLocalhostOrigins = []string{ + "http://127.0.0.1:", + "https://127.0.0.1:", + "http://localhost:", + "https://localhost:", +} + func addCORSFromEnv(c *cmdsHttp.ServerConfig) { origin := os.Getenv(originEnvKey) if origin != "" { @@ -58,6 +66,39 @@ func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) { c.Headers = nc.API.HTTPHeaders } +func addCORSDefaults(c *cmdsHttp.ServerConfig) { + // by default use localhost origins + if len(c.CORSOpts.AllowedOrigins) == 0 { + c.CORSOpts.AllowedOrigins = defaultLocalhostOrigins + } + + // by default, use GET, PUT, POST + if len(c.CORSOpts.AllowedMethods) == 0 { + c.CORSOpts.AllowedMethods = []string{"GET", "POST", "PUT"} + } +} + +func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) { + + // we have to grab the port from an addr, which may be an ip6 addr. + // TODO: this should take multiaddrs and derive port from there. + port := "" + if tcpaddr, ok := addr.(*net.TCPAddr); ok { + port = strconv.Itoa(tcpaddr.Port) + } else if udpaddr, ok := addr.(*net.UDPAddr); ok { + port = strconv.Itoa(udpaddr.Port) + } + + // we're listening on tcp/udp with ports. ("udp!?" you say? yeah... it happens...) + for i, o := range c.CORSOpts.AllowedOrigins { + // TODO: allow replacing . tricky, ip4 and ip6 and hostnames... + if port != "" { + o = strings.Replace(o, "", port, -1) + } + c.CORSOpts.AllowedOrigins[i] = o + } +} + func CommandsOption(cctx commands.Context) ServeOption { return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { @@ -69,6 +110,8 @@ func CommandsOption(cctx commands.Context) ServeOption { addHeadersFromConfig(cfg, n.Repo.Config()) addCORSFromEnv(cfg) + addCORSDefaults(cfg) + patchCORSVars(cfg, l.Addr()) cmdHandler := cmdsHttp.NewHandler(cctx, corecommands.Root, cfg) mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)