github.com/avenga/couper@v1.12.2/config/configload/endpoint.go (about) 1 package configload 2 3 import ( 4 "net/http" 5 6 "github.com/hashicorp/hcl/v2" 7 "github.com/hashicorp/hcl/v2/hclsyntax" 8 "github.com/zclconf/go-cty/cty" 9 10 "github.com/avenga/couper/config" 11 hclbody "github.com/avenga/couper/config/body" 12 "github.com/avenga/couper/config/configload/collect" 13 "github.com/avenga/couper/errors" 14 ) 15 16 func newCatchAllEndpoint() *config.Endpoint { 17 responseBody := hclbody.NewHCLSyntaxBodyWithAttr("status", cty.NumberIntVal(http.StatusNotFound), hcl.Range{}) 18 19 return &config.Endpoint{ 20 Pattern: "/**", 21 Remain: &hclsyntax.Body{}, 22 Response: &config.Response{ 23 Remain: responseBody, 24 }, 25 } 26 } 27 28 func refineEndpoints(helper *helper, endpoints config.Endpoints, checkPathPattern bool, definedACs map[string]struct{}) error { 29 var err error 30 31 for _, ep := range endpoints { 32 if checkPathPattern && ep.Pattern == "" { 33 var r *hcl.Range 34 if ep.Remain != nil { 35 r = getRange(ep.HCLBody()) 36 } 37 return newDiagErr(r, "endpoint: missing path pattern") 38 } 39 40 endpointBody := ep.HCLBody() 41 if definedACs != nil { 42 if err := checkReferencedAccessControls(endpointBody, ep.AccessControl, ep.DisableAccessControl, definedACs); err != nil { 43 return err 44 } 45 } 46 47 rp := endpointBody.Attributes["required_permission"] 48 if rp != nil { 49 ep.RequiredPermission = rp.Expr 50 } 51 52 if checkPathPattern && ep.AllowedMethods != nil && len(ep.AllowedMethods) > 0 { 53 if err = validMethods(ep.AllowedMethods, endpointBody.Attributes["allowed_methods"]); err != nil { 54 return err 55 } 56 } 57 58 if checkPathPattern && len(ep.Proxies)+len(ep.Requests) == 0 && ep.Response == nil { 59 r := endpointBody.SrcRange 60 return newDiagErr(&r, 61 "endpoint: missing 'default' proxy or request block, or a response definition", 62 ) 63 } 64 65 proxyRequestLabelRequired := len(ep.Proxies)+len(ep.Requests) > 1 66 67 names := map[string]*hclsyntax.Body{} 68 unique := map[string]struct{}{} 69 subject := ep.HCLBody().SrcRange 70 71 for _, proxyConfig := range ep.Proxies { 72 if proxyConfig.Name == "" { 73 proxyConfig.Name = config.DefaultNameLabel 74 } 75 76 names[proxyConfig.Name] = proxyConfig.HCLBody() 77 78 subject = proxyConfig.HCLBody().SrcRange 79 if err = validLabel(proxyConfig.Name, &subject); err != nil { 80 return err 81 } 82 83 if proxyRequestLabelRequired { 84 if err = uniqueLabelName("proxy and request", unique, proxyConfig.Name, &subject); err != nil { 85 return err 86 } 87 } 88 89 wsEnabled, wsBody, wsErr := getWebsocketsConfig(proxyConfig) 90 if wsErr != nil { 91 return wsErr 92 } 93 94 if wsEnabled { 95 if proxyConfig.Name != config.DefaultNameLabel { 96 return errors.Configuration.Message("websockets attribute or block is only allowed in a 'default' proxy block") 97 } 98 if proxyRequestLabelRequired || ep.Response != nil { 99 return errors.Configuration.Message("websockets are allowed in the endpoint; other 'proxy', 'request' or 'response' blocks are not allowed") 100 } 101 102 if wsBody != nil { 103 proxyConfig.Remain = hclbody.MergeBodies(proxyConfig.HCLBody(), wsBody, true) 104 } 105 } 106 107 proxyConfig.Backend, err = PrepareBackend(helper, "", "", proxyConfig) 108 if err != nil { 109 return err 110 } 111 } 112 113 for _, reqConfig := range ep.Requests { 114 if reqConfig.Name == "" { 115 reqConfig.Name = config.DefaultNameLabel 116 } 117 118 names[reqConfig.Name] = reqConfig.HCLBody() 119 120 subject = reqConfig.HCLBody().SrcRange 121 if err = validLabel(reqConfig.Name, &subject); err != nil { 122 return err 123 } 124 125 if proxyRequestLabelRequired { 126 if err = uniqueLabelName("proxy and request", unique, reqConfig.Name, &subject); err != nil { 127 return err 128 } 129 } 130 131 // remap request specific names for headers and query to well known ones 132 reqBody := reqConfig.HCLBody() 133 if err = verifyBodyAttributes(request, reqBody); err != nil { 134 return err 135 } 136 137 hclbody.RenameAttribute(reqBody, "headers", "set_request_headers") 138 hclbody.RenameAttribute(reqBody, "query_params", "set_query_params") 139 140 reqConfig.Backend, err = PrepareBackend(helper, "", "", reqConfig) 141 if err != nil { 142 return err 143 } 144 } 145 146 if ep.Response != nil { 147 if err = verifyResponseBodyAttrs(ep.Response.HCLBody()); err != nil { 148 return err 149 } 150 } 151 152 if _, ok := names[config.DefaultNameLabel]; checkPathPattern && !ok && ep.Response == nil { 153 return newDiagErr(&subject, "Missing a 'default' proxy or request definition, or a response block") 154 } 155 156 if err = buildSequences(names, ep); err != nil { 157 return err 158 } 159 160 epErrorHandler := collect.ErrorHandlerSetters(ep) 161 if err = configureErrorHandler(epErrorHandler, helper); err != nil { 162 return err 163 } 164 } 165 166 return nil 167 } 168 169 func getWebsocketsConfig(proxyConfig *config.Proxy) (bool, *hclsyntax.Body, error) { 170 hasWebsocketBlocks := len(hclbody.BlocksOfType(proxyConfig.HCLBody(), "websockets")) > 0 171 if proxyConfig.Websockets != nil && hasWebsocketBlocks { 172 hr := proxyConfig.HCLBody().Attributes["websockets"].SrcRange 173 return false, nil, newDiagErr(&hr, "either websockets attribute or block is allowed") 174 } 175 176 if proxyConfig.Websockets != nil { 177 var body *hclsyntax.Body 178 179 if *proxyConfig.Websockets { 180 block := &hclsyntax.Block{ 181 Type: "websockets", 182 Body: &hclsyntax.Body{}, 183 } 184 185 body = &hclsyntax.Body{Blocks: []*hclsyntax.Block{block}} 186 } 187 188 return *proxyConfig.Websockets, body, nil 189 } 190 191 return hasWebsocketBlocks, nil, nil 192 }