github.com/avenga/couper@v1.12.2/handler/producer/request.go (about) 1 package producer 2 3 import ( 4 "context" 5 "net/http" 6 "strings" 7 8 "github.com/hashicorp/hcl/v2/hclsyntax" 9 semconv "go.opentelemetry.io/otel/semconv/v1.12.0" 10 "go.opentelemetry.io/otel/trace" 11 12 "github.com/avenga/couper/config/request" 13 "github.com/avenga/couper/eval" 14 "github.com/avenga/couper/internal/seetie" 15 "github.com/avenga/couper/telemetry" 16 ) 17 18 // Request represents the producer <Request> object. 19 type Request struct { 20 Backend http.RoundTripper 21 Context *hclsyntax.Body 22 Name string // label 23 dependsOn string 24 } 25 26 func (r *Request) Produce(req *http.Request) *Result { 27 ctx := req.Context() 28 var rootSpan trace.Span 29 ctx, rootSpan = telemetry.NewSpanFromContext(ctx, "requests", trace.WithSpanKind(trace.SpanKindProducer)) 30 31 hclCtx := eval.ContextFromRequest(req).HCLContextSync() // also synced for requests due to sequence case 32 33 // span end by result reader 34 outCtx, span := telemetry.NewSpanFromContext(withRoundTripName(ctx, r.Name), r.Name, trace.WithSpanKind(trace.SpanKindClient)) 35 if r.dependsOn != "" { 36 outCtx = context.WithValue(outCtx, request.EndpointSequenceDependsOn, r.dependsOn) 37 } 38 39 methodVal, err := eval.ValueFromBodyAttribute(hclCtx, r.Context, "method") 40 if err != nil { 41 return &Result{Err: err} 42 } 43 method := seetie.ValueToString(methodVal) 44 45 outreq := req.Clone(req.Context()) 46 removeHost(outreq) 47 48 url, err := NewURLFromAttribute(hclCtx, r.Context, "url", outreq) 49 if err != nil { 50 return &Result{Err: err} 51 } 52 53 body, defaultContentType, err := eval.GetBody(hclCtx, r.Context) 54 if err != nil { 55 return &Result{Err: err} 56 } 57 58 if method == "" { 59 method = http.MethodGet 60 61 if len(body) > 0 { 62 method = http.MethodPost 63 } 64 } 65 66 outreq, err = http.NewRequest(strings.ToUpper(method), url.String(), nil) 67 if err != nil { 68 return &Result{Err: err} 69 } 70 71 expStatusVal, err := eval.ValueFromBodyAttribute(hclCtx, r.Context, "expected_status") 72 if err != nil { 73 return &Result{Err: err} 74 } 75 76 outCtx = context.WithValue(outCtx, request.EndpointExpectedStatus, seetie.ValueToIntSlice(expStatusVal)) 77 78 if defaultContentType != "" { 79 outreq.Header.Set("Content-Type", defaultContentType) 80 } 81 82 eval.SetBody(outreq, []byte(body)) 83 84 outreq = outreq.WithContext(outCtx) 85 err = eval.ApplyRequestContext(hclCtx, r.Context, outreq) 86 if err != nil { 87 return &Result{Err: err} 88 } 89 90 span.SetAttributes(semconv.HTTPClientAttributesFromHTTPRequest(outreq)...) 91 92 result := roundtrip(r.Backend, outreq) 93 94 rootSpan.End() 95 return result 96 } 97 98 func (r *Request) SetDependsOn(ps string) { 99 r.dependsOn = ps 100 } 101 102 func withRoundTripName(ctx context.Context, name string) context.Context { 103 n := name 104 if n == "" { 105 n = "default" 106 } 107 return context.WithValue(ctx, request.RoundTripName, n) 108 }