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  }