github.com/avenga/couper@v1.12.2/config/runtime/error_handler.go (about)

     1  package runtime
     2  
     3  import (
     4  	"net/http"
     5  	"reflect"
     6  
     7  	"github.com/hashicorp/hcl/v2"
     8  	"github.com/hashicorp/hcl/v2/hclsyntax"
     9  	"github.com/sirupsen/logrus"
    10  
    11  	"github.com/avenga/couper/config"
    12  	"github.com/avenga/couper/errors"
    13  	"github.com/avenga/couper/eval"
    14  	"github.com/avenga/couper/eval/buffer"
    15  	"github.com/avenga/couper/handler"
    16  )
    17  
    18  func newErrorHandler(ctx *hcl.EvalContext, conf *config.Couper, opts *protectedOptions, log *logrus.Entry,
    19  	defs ACDefinitions, references ...string) (http.Handler, buffer.Option, error) {
    20  	kindsHandler := map[string]http.Handler{}
    21  	var ehBufferOption buffer.Option
    22  	for _, ref := range references {
    23  		definition, ok := defs[ref]
    24  		if !ok {
    25  			continue
    26  		}
    27  
    28  		handlersPerKind := make(map[string]*config.ErrorHandler)
    29  		for _, h := range definition.ErrorHandler {
    30  			for _, k := range h.Kinds {
    31  				handlersPerKind[k] = h
    32  			}
    33  		}
    34  
    35  		if superKindMap, mapExists := errors.SuperTypesMapsByContext[ref]; mapExists {
    36  			// expand super-kinds:
    37  			// * set super-kind error handler for mapped sub-kinds, if no error handler for this sub-kind is already set
    38  			// * remove super-kind error handler for super-kind
    39  			for superKind, subKinds := range superKindMap {
    40  				if skHandler, skExists := handlersPerKind[superKind]; skExists {
    41  					for _, subKind := range subKinds {
    42  						if _, exists := handlersPerKind[subKind]; !exists {
    43  							handlersPerKind[subKind] = skHandler
    44  						}
    45  					}
    46  
    47  					delete(handlersPerKind, superKind)
    48  				}
    49  			}
    50  		}
    51  
    52  		for k, h := range handlersPerKind {
    53  			contextBody := h.HCLBody()
    54  
    55  			epConf := &config.Endpoint{
    56  				Remain:    contextBody,
    57  				Proxies:   h.Proxies,
    58  				ErrorFile: h.ErrorFile,
    59  				Requests:  h.Requests,
    60  				Response:  h.Response,
    61  			}
    62  
    63  			emptyBody := &hclsyntax.Body{}
    64  			if epConf.Response == nil { // Set dummy resp to skip related requirement checks, allowed for error_handler.
    65  				epConf.Response = &config.Response{Remain: emptyBody}
    66  			}
    67  
    68  			epOpts, err := NewEndpointOptions(ctx, epConf, nil, opts.srvOpts, log, conf, opts.memStore)
    69  			if err != nil {
    70  				return nil, buffer.None, err
    71  			}
    72  			if epOpts.ErrorTemplate == nil || h.ErrorFile == "" {
    73  				epOpts.ErrorTemplate = opts.epOpts.ErrorTemplate
    74  			}
    75  
    76  			epOpts.ErrorTemplate = epOpts.ErrorTemplate.WithContextFunc(func(rw http.ResponseWriter, r *http.Request) {
    77  				beresp := &http.Response{Header: rw.Header()}
    78  				_ = eval.ApplyResponseContext(eval.ContextFromRequest(r).HCLContextSync(), contextBody, beresp)
    79  			})
    80  
    81  			if epOpts.Response != nil && reflect.DeepEqual(epOpts.Response.Context, emptyBody) {
    82  				epOpts.Response = nil
    83  			}
    84  
    85  			epOpts.LogHandlerKind = "error_" + k
    86  			epOpts.IsErrorHandler = true
    87  			kindsHandler[k] = handler.NewEndpoint(epOpts, log, nil)
    88  			ehBufferOption |= epOpts.BufferOpts
    89  		}
    90  	}
    91  
    92  	return handler.NewErrorHandler(kindsHandler, opts.epOpts.ErrorTemplate), ehBufferOption, nil
    93  }