diff --git a/go.mod b/go.mod index 2583d90db..6139085ff 100644 --- a/go.mod +++ b/go.mod @@ -31,12 +31,12 @@ require ( github.com/moby/moby v20.10.22+incompatible github.com/nitrictech/go-mods-direct v0.0.0-20221212215233-12bba0c74a7f github.com/nitrictech/nitric/cloud/aws v0.0.0-20230718015326-80e1b8640347 - github.com/nitrictech/nitric/core v0.0.0-20230928000037-7261774edded + github.com/nitrictech/nitric/core v0.0.0-20231107061027-b246cc30347a github.com/pkg/errors v0.9.1 github.com/pterm/pterm v0.12.54 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.8.4 - github.com/valyala/fasthttp v1.47.0 + github.com/valyala/fasthttp v1.50.0 golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea golang.org/x/mod v0.10.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect @@ -54,7 +54,7 @@ require ( github.com/fasthttp/websocket v1.5.3 github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e github.com/mitchellh/mapstructure v1.5.0 - github.com/nitrictech/nitric/cloud/common v0.0.0-20230710061936-f8ccb102d31b + github.com/nitrictech/nitric/cloud/common v0.0.0-20231107061027-b246cc30347a github.com/nitrictech/pearls v0.0.0-20230927035425-dc9ec468bb24 github.com/olahol/melody v1.1.3 github.com/samber/lo v1.38.1 diff --git a/go.sum b/go.sum index aa9734684..f6164d6ee 100644 --- a/go.sum +++ b/go.sum @@ -1649,12 +1649,10 @@ github.com/nitrictech/go-mods-direct v0.0.0-20221212215233-12bba0c74a7f h1:awate github.com/nitrictech/go-mods-direct v0.0.0-20221212215233-12bba0c74a7f/go.mod h1:jqImBS1Tsk12vV2Gk7yP9+jajhpG+cHXrg7UhVxFruQ= github.com/nitrictech/nitric/cloud/aws v0.0.0-20230718015326-80e1b8640347 h1:/9BIGv7ed6qWkeXchLXX0FIMqDp+7khBY5WlbHcJJEI= github.com/nitrictech/nitric/cloud/aws v0.0.0-20230718015326-80e1b8640347/go.mod h1:HTToJAkzkFpLszJp2Vy9vDoplEOmWcuv86wW1o0J3YY= -github.com/nitrictech/nitric/cloud/common v0.0.0-20230710061936-f8ccb102d31b h1:9pAldJTjky0UlyEpibr355HsZWXwZdy7ZEvctVyV48U= -github.com/nitrictech/nitric/cloud/common v0.0.0-20230710061936-f8ccb102d31b/go.mod h1:nz8/tXPMb5o2yv8g4+NsvDu0dPyrY6iZyXZ1EprenlM= -github.com/nitrictech/nitric/core v0.0.0-20230718015326-80e1b8640347 h1:5c6IDgweLzybxniL9OkTdk9AvheDP2F6p1sFOGteICI= -github.com/nitrictech/nitric/core v0.0.0-20230718015326-80e1b8640347/go.mod h1:iz/seHApmfpML52NVjankr+9izPddUUwZHdcSQEvm9E= -github.com/nitrictech/nitric/core v0.0.0-20230928000037-7261774edded h1:Nm+S/GAnvKJfQHu75MY7o5LIJUukiM4WNnFj43gb2OA= -github.com/nitrictech/nitric/core v0.0.0-20230928000037-7261774edded/go.mod h1:iz/seHApmfpML52NVjankr+9izPddUUwZHdcSQEvm9E= +github.com/nitrictech/nitric/cloud/common v0.0.0-20231107061027-b246cc30347a h1:S6wm26B6MIZnpf1h4ZPv0+R9nrbh2Fd+nFfSgVMK01M= +github.com/nitrictech/nitric/cloud/common v0.0.0-20231107061027-b246cc30347a/go.mod h1:+X62o2IvXWO1jw3758kdy+JWDCd5f1hCgqdPa4/Gtbo= +github.com/nitrictech/nitric/core v0.0.0-20231107061027-b246cc30347a h1:eBANKkpskCtrUot9B3rbfwKvdLge4hnIsmFU7pG8QPc= +github.com/nitrictech/nitric/core v0.0.0-20231107061027-b246cc30347a/go.mod h1:s1jivAiMfES33W80gdV/YiVPpUpDQrpnNpu9gdYyMKM= github.com/nitrictech/pearls v0.0.0-20230927035425-dc9ec468bb24 h1:Jb61oiwZoyMDqmLzNdgHjKYDLtDmbeb4GYEGSK/ZqgA= github.com/nitrictech/pearls v0.0.0-20230927035425-dc9ec468bb24/go.mod h1:IXCOsqYAR5KRqXbbXnHkMp9NJrfY0qOeV3IcbBR5uSA= github.com/nitrictech/protoutils v0.0.0-20220321044654-02667a814cdf h1:8MB8W8ylM8sCM2COGfiO39/tB6BTdiawLszaUGCNL5w= @@ -2143,8 +2141,8 @@ github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842 github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= -github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= +github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/viant/assertly v0.5.4 h1:5Hh4U3pLZa6uhCFAGpYOxck/8l9TZczEzoHNfJAhHEQ= github.com/viant/ptrie v0.3.0 h1:SDaRd7Gqr1+ItCNz0GpTxRdK21nOfqjV6YtBm9jGlMY= github.com/viant/ptrie v0.3.0/go.mod h1:VguMnbGfz95Zw+V5VarYSqtqslDxJbOv++xLzxkMhec= diff --git a/pkg/codeconfig/api.go b/pkg/codeconfig/api.go index 79d124b91..85c2d70a8 100644 --- a/pkg/codeconfig/api.go +++ b/pkg/codeconfig/api.go @@ -29,6 +29,7 @@ type Api struct { parent *FunctionDependencies securityDefinitions map[string]*v1.ApiSecurityDefinition security map[string][]string + cors *v1.ApiCorsDefinition workers []*v1.ApiWorker lock sync.RWMutex } @@ -108,3 +109,10 @@ func (a *Api) AddSecurity(name string, scopes []string) { a.security[name] = []string{} } } + +func (a *Api) AddCors(cors *v1.ApiCorsDefinition) { + a.lock.Lock() + defer a.lock.Unlock() + + a.cors = cors +} diff --git a/pkg/codeconfig/function.go b/pkg/codeconfig/function.go index 1b298b4b5..c9385195b 100644 --- a/pkg/codeconfig/function.go +++ b/pkg/codeconfig/function.go @@ -77,7 +77,7 @@ func (a *FunctionDependencies) AddApiSecurityDefinitions(name string, sds map[st } } -func (a *FunctionDependencies) AddApiSecurity(name string, security map[string]*v1.ApiScopes) { +func (a *FunctionDependencies) AddApiSecurity(name string, security map[string]*v1.ApiScopes, cors *v1.ApiCorsDefinition) { a.lock.Lock() defer a.lock.Unlock() @@ -85,6 +85,8 @@ func (a *FunctionDependencies) AddApiSecurity(name string, security map[string]* a.apis[name] = newApi(a) } + a.apis[name].AddCors(cors) + for n, scopes := range security { a.apis[name].AddSecurity(n, scopes.Scopes) } diff --git a/pkg/codeconfig/server.go b/pkg/codeconfig/server.go index 10222b92e..2a329508c 100644 --- a/pkg/codeconfig/server.go +++ b/pkg/codeconfig/server.go @@ -87,7 +87,7 @@ func (s *Server) Declare(ctx context.Context, req *v1.ResourceDeclareRequest) (* s.function.AddSecret(req.Resource.Name, req.GetSecret()) case v1.ResourceType_Api: s.function.AddApiSecurityDefinitions(req.Resource.Name, req.GetApi().SecurityDefinitions) - s.function.AddApiSecurity(req.Resource.Name, req.GetApi().Security) + s.function.AddApiSecurity(req.Resource.Name, req.GetApi().Security, req.GetApi().GetCors()) case v1.ResourceType_Websocket: // TODO: Add websocket configuration here when available break diff --git a/pkg/codeconfig/uprequest.go b/pkg/codeconfig/uprequest.go index 6f4c617b5..02ea87c14 100644 --- a/pkg/codeconfig/uprequest.go +++ b/pkg/codeconfig/uprequest.go @@ -195,7 +195,7 @@ func (c *codeConfig) ToUpRequest() (*deploy.DeployUpRequest, error) { builder.set(res) } - for k := range f.apis { + for k, api := range f.apis { spec, err := c.apiSpec(k, nil) if err != nil { errs.Push(fmt.Errorf("could not build spec for api: %s; %w", k, err)) @@ -216,6 +216,7 @@ func (c *codeConfig) ToUpRequest() (*deploy.DeployUpRequest, error) { Document: &deploy.Api_Openapi{ Openapi: string(apiBody), }, + Cors: api.cors, }, }, }) diff --git a/pkg/run/gateway.go b/pkg/run/gateway.go index 50a7108cd..000f3ef2f 100644 --- a/pkg/run/gateway.go +++ b/pkg/run/gateway.go @@ -37,6 +37,7 @@ import ( "github.com/nitrictech/cli/pkg/history" "github.com/nitrictech/cli/pkg/project" "github.com/nitrictech/cli/pkg/utils" + "github.com/nitrictech/nitric/cloud/common/cors" base_http "github.com/nitrictech/nitric/cloud/common/runtime/gateway" v1 "github.com/nitrictech/nitric/core/pkg/api/nitric/v1" "github.com/nitrictech/nitric/core/pkg/plugins/gateway" @@ -71,10 +72,11 @@ type BaseHttpGateway struct { websocketPlugin *RunWebsocketService serviceListener net.Listener gateway.UnimplementedGatewayPlugin - stop chan bool - pool pool.WorkerPool - project *project.Project - dash *dashboard.Dashboard + stop chan bool + pool pool.WorkerPool + project *project.Project + dash *dashboard.Dashboard + corsCache map[string]map[string]string } var _ gateway.GatewayService = &BaseHttpGateway{} @@ -213,6 +215,8 @@ func (s *BaseHttpGateway) handleHttpProxyRequest(idx int) fasthttp.RequestHandle } func (s *BaseHttpGateway) handleApiHttpRequest(idx int) fasthttp.RequestHandler { + corsMiddleware := cors.CreateCorsMiddleware(s.corsCache) + return func(ctx *fasthttp.RequestCtx) { if idx >= len(s.apis) { ctx.Error("Sorry, nitric is listening on this port but is waiting for an API to be available to handle, you may have removed an API during development this port will be assigned to an API when one becomes available", 404) @@ -221,6 +225,10 @@ func (s *BaseHttpGateway) handleApiHttpRequest(idx int) fasthttp.RequestHandler apiName := s.apis[idx] + ctx.Request.Header.Add("X-Nitric-Api", apiName) + + corsMiddleware(ctx, s.pool) + headerMap := base_http.HttpHeadersToMap(&ctx.Request.Header) headers := map[string]*v1.HeaderValue{} @@ -264,6 +272,12 @@ func (s *BaseHttpGateway) handleApiHttpRequest(idx int) fasthttp.RequestHandler Trigger: httpTrigger, Filter: apiWorkerFilter(apiName), }) + + if worker == nil && s.corsCache[apiName] != nil && string(ctx.Request.Header.Method()) == "OPTIONS" { + ctx.Response.SetStatusCode(204) + return + } + if err != nil { ctx.Redirect(fmt.Sprintf("http://localhost:%v/not-found", s.dash.GetPort()), fasthttp.StatusTemporaryRedirect) return @@ -814,10 +828,25 @@ func (s *BaseHttpGateway) Stop() error { return nil } +func (s *BaseHttpGateway) AddCors(apiName string, def *v1.ApiCorsDefinition) { + if def == nil { + s.corsCache[apiName] = nil + return + } + + headers, err := cors.GetCorsHeaders(def) + if err != nil { + s.corsCache[apiName] = nil + } else { + s.corsCache[apiName] = *headers + } +} + // Create new HTTP gateway // XXX: No External Args for function atm (currently the plugin loader does not pass any argument information) func NewGateway(wsPlugin *RunWebsocketService) (*BaseHttpGateway, error) { return &BaseHttpGateway{ websocketPlugin: wsPlugin, + corsCache: map[string]map[string]string{}, }, nil } diff --git a/pkg/run/resources.go b/pkg/run/resources.go index 6a42d43ba..151370f0c 100644 --- a/pkg/run/resources.go +++ b/pkg/run/resources.go @@ -87,6 +87,8 @@ func (r *RunResourcesService) Declare(ctx context.Context, req resource.Resource resource := req.Resource switch resource.Type { + case v1.ResourceType_Api: + r.ls.gateway.AddCors(resource.GetName(), req.GetApi().GetCors()) case v1.ResourceType_Bucket: r.ls.dashboard.AddBucket(resource.GetName()) }