1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-07-03 13:00:37 +08:00

Merge pull request #1835 from miolini/master

fix races in http cors
This commit is contained in:
Juan Benet
2015-10-20 12:47:06 -07:00
3 changed files with 76 additions and 33 deletions

View File

@ -10,6 +10,7 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync"
cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors" cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
@ -67,8 +68,11 @@ type ServerConfig struct {
// Headers is an optional map of headers that is written out. // Headers is an optional map of headers that is written out.
Headers map[string][]string Headers map[string][]string
// CORSOpts is a set of options for CORS headers. // cORSOpts is a set of options for CORS headers.
CORSOpts *cors.Options cORSOpts *cors.Options
// cORSOptsRWMutex is a RWMutex for read/write CORSOpts
cORSOptsRWMutex sync.RWMutex
} }
func skipAPIHeader(h string) bool { func skipAPIHeader(h string) bool {
@ -92,7 +96,7 @@ func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handle
// Wrap the internal handler with CORS handling-middleware. // Wrap the internal handler with CORS handling-middleware.
// Create a handler for the API. // Create a handler for the API.
internal := internalHandler{ctx, root, cfg} internal := internalHandler{ctx, root, cfg}
c := cors.New(*cfg.CORSOpts) c := cors.New(*cfg.cORSOpts)
return &Handler{internal, c.Handler(internal)} return &Handler{internal, c.Handler(internal)}
} }
@ -255,6 +259,51 @@ func sanitizedErrStr(err error) string {
return s return s
} }
func NewServerConfig() *ServerConfig {
cfg := new(ServerConfig)
cfg.cORSOpts = new(cors.Options)
return cfg
}
func (cfg ServerConfig) AllowedOrigins() []string {
cfg.cORSOptsRWMutex.RLock()
defer cfg.cORSOptsRWMutex.RUnlock()
return cfg.cORSOpts.AllowedOrigins
}
func (cfg *ServerConfig) SetAllowedOrigins(origins ...string) {
cfg.cORSOptsRWMutex.Lock()
defer cfg.cORSOptsRWMutex.Unlock()
cfg.cORSOpts.AllowedOrigins = origins
}
func (cfg *ServerConfig) AppendAllowedOrigins(origins ...string) {
cfg.cORSOptsRWMutex.Lock()
defer cfg.cORSOptsRWMutex.Unlock()
cfg.cORSOpts.AllowedOrigins = append(cfg.cORSOpts.AllowedOrigins, origins...)
}
func (cfg ServerConfig) AllowedMethods() []string {
cfg.cORSOptsRWMutex.RLock()
defer cfg.cORSOptsRWMutex.RUnlock()
return []string(cfg.cORSOpts.AllowedMethods)
}
func (cfg *ServerConfig) SetAllowedMethods(methods ...string) {
cfg.cORSOptsRWMutex.Lock()
defer cfg.cORSOptsRWMutex.Unlock()
if cfg.cORSOpts == nil {
cfg.cORSOpts = new(cors.Options)
}
cfg.cORSOpts.AllowedMethods = methods
}
func (cfg *ServerConfig) SetAllowCredentials(flag bool) {
cfg.cORSOptsRWMutex.Lock()
defer cfg.cORSOptsRWMutex.Unlock()
cfg.cORSOpts.AllowCredentials = flag
}
// allowOrigin just stops the request if the origin is not allowed. // allowOrigin just stops the request if the origin is not allowed.
// the CORS middleware apparently does not do this for us... // the CORS middleware apparently does not do this for us...
func allowOrigin(r *http.Request, cfg *ServerConfig) bool { func allowOrigin(r *http.Request, cfg *ServerConfig) bool {
@ -266,8 +315,8 @@ func allowOrigin(r *http.Request, cfg *ServerConfig) bool {
if origin == "" { if origin == "" {
return true return true
} }
origins := cfg.AllowedOrigins()
for _, o := range cfg.CORSOpts.AllowedOrigins { for _, o := range origins {
if o == "*" { // ok! you asked for it! if o == "*" { // ok! you asked for it!
return true return true
} }
@ -308,7 +357,8 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
// check CORS ACAOs and pretend Referer works like an origin. // check CORS ACAOs and pretend Referer works like an origin.
// this is valid for many (most?) sane uses of the API in // this is valid for many (most?) sane uses of the API in
// other applications, and will have the desired effect. // other applications, and will have the desired effect.
for _, o := range cfg.CORSOpts.AllowedOrigins { origins := cfg.AllowedOrigins()
for _, o := range origins {
if o == "*" { // ok! you asked for it! if o == "*" { // ok! you asked for it!
return true return true
} }

View File

@ -6,8 +6,6 @@ import (
"net/url" "net/url"
"testing" "testing"
cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
cmds "github.com/ipfs/go-ipfs/commands" cmds "github.com/ipfs/go-ipfs/commands"
ipfscmd "github.com/ipfs/go-ipfs/core/commands" ipfscmd "github.com/ipfs/go-ipfs/core/commands"
coremock "github.com/ipfs/go-ipfs/core/mock" coremock "github.com/ipfs/go-ipfs/core/mock"
@ -28,12 +26,10 @@ func assertStatus(t *testing.T, actual, expected int) {
} }
func originCfg(origins []string) *ServerConfig { func originCfg(origins []string) *ServerConfig {
return &ServerConfig{ cfg := NewServerConfig()
CORSOpts: &cors.Options{ cfg.SetAllowedOrigins(origins...)
AllowedOrigins: origins, cfg.SetAllowedMethods("GET", "PUT", "POST")
AllowedMethods: []string{"GET", "PUT", "POST"}, return cfg
},
}
} }
type testCase struct { type testCase struct {

View File

@ -7,8 +7,6 @@ import (
"strconv" "strconv"
"strings" "strings"
cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
commands "github.com/ipfs/go-ipfs/commands" commands "github.com/ipfs/go-ipfs/commands"
cmdsHttp "github.com/ipfs/go-ipfs/commands/http" cmdsHttp "github.com/ipfs/go-ipfs/commands/http"
core "github.com/ipfs/go-ipfs/core" core "github.com/ipfs/go-ipfs/core"
@ -41,10 +39,10 @@ func addCORSFromEnv(c *cmdsHttp.ServerConfig) {
origin := os.Getenv(originEnvKey) origin := os.Getenv(originEnvKey)
if origin != "" { if origin != "" {
log.Warning(originEnvKeyDeprecate) log.Warning(originEnvKeyDeprecate)
if c.CORSOpts == nil { if len(c.AllowedOrigins()) == 0 {
c.CORSOpts.AllowedOrigins = []string{origin} c.SetAllowedOrigins([]string{origin}...)
} }
c.CORSOpts.AllowedOrigins = append(c.CORSOpts.AllowedOrigins, origin) c.AppendAllowedOrigins(origin)
} }
} }
@ -52,14 +50,14 @@ func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
log.Info("Using API.HTTPHeaders:", nc.API.HTTPHeaders) log.Info("Using API.HTTPHeaders:", nc.API.HTTPHeaders)
if acao := nc.API.HTTPHeaders[cmdsHttp.ACAOrigin]; acao != nil { if acao := nc.API.HTTPHeaders[cmdsHttp.ACAOrigin]; acao != nil {
c.CORSOpts.AllowedOrigins = acao c.SetAllowedOrigins(acao...)
} }
if acam := nc.API.HTTPHeaders[cmdsHttp.ACAMethods]; acam != nil { if acam := nc.API.HTTPHeaders[cmdsHttp.ACAMethods]; acam != nil {
c.CORSOpts.AllowedMethods = acam c.SetAllowedMethods(acam...)
} }
if acac := nc.API.HTTPHeaders[cmdsHttp.ACACredentials]; acac != nil { if acac := nc.API.HTTPHeaders[cmdsHttp.ACACredentials]; acac != nil {
for _, v := range acac { for _, v := range acac {
c.CORSOpts.AllowCredentials = (strings.ToLower(v) == "true") c.SetAllowCredentials(strings.ToLower(v) == "true")
} }
} }
@ -68,13 +66,13 @@ func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
func addCORSDefaults(c *cmdsHttp.ServerConfig) { func addCORSDefaults(c *cmdsHttp.ServerConfig) {
// by default use localhost origins // by default use localhost origins
if len(c.CORSOpts.AllowedOrigins) == 0 { if len(c.AllowedOrigins()) == 0 {
c.CORSOpts.AllowedOrigins = defaultLocalhostOrigins c.SetAllowedOrigins(defaultLocalhostOrigins...)
} }
// by default, use GET, PUT, POST // by default, use GET, PUT, POST
if len(c.CORSOpts.AllowedMethods) == 0 { if len(c.AllowedMethods()) == 0 {
c.CORSOpts.AllowedMethods = []string{"GET", "POST", "PUT"} c.SetAllowedMethods("GET", "POST", "PUT")
} }
} }
@ -90,23 +88,22 @@ func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) {
} }
// we're listening on tcp/udp with ports. ("udp!?" you say? yeah... it happens...) // we're listening on tcp/udp with ports. ("udp!?" you say? yeah... it happens...)
for i, o := range c.CORSOpts.AllowedOrigins { origins := c.AllowedOrigins()
for i, o := range origins {
// TODO: allow replacing <host>. tricky, ip4 and ip6 and hostnames... // TODO: allow replacing <host>. tricky, ip4 and ip6 and hostnames...
if port != "" { if port != "" {
o = strings.Replace(o, "<port>", port, -1) o = strings.Replace(o, "<port>", port, -1)
} }
c.CORSOpts.AllowedOrigins[i] = o origins[i] = o
} }
c.SetAllowedOrigins(origins...)
} }
func commandsOption(cctx commands.Context, command *commands.Command) ServeOption { func commandsOption(cctx commands.Context, command *commands.Command) ServeOption {
return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
cfg := &cmdsHttp.ServerConfig{ cfg := cmdsHttp.NewServerConfig()
CORSOpts: &cors.Options{ cfg.SetAllowedMethods("GET", "POST", "PUT")
AllowedMethods: []string{"GET", "POST", "PUT"},
},
}
rcfg, err := n.Repo.Config() rcfg, err := n.Repo.Config()
if err != nil { if err != nil {
return nil, err return nil, err