@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 128 lines 4.59 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package reverseproxy
16
17 import (
18 "context"
19 "net"
20 "net/http"
21 "net/http/httptest"
22 "strings"
23 "testing"
24
25 "go.opentelemetry.io/otel/attribute"
26 "go.opentelemetry.io/otel/codes"
27 sdktrace "go.opentelemetry.io/otel/sdk/trace"
28 "go.opentelemetry.io/otel/sdk/trace/tracetest"
29 "go.opentelemetry.io/otel/trace"
30
31 "github.com/caddyserver/caddy/v2"
32 )
33
34 func TestUpstreamClientSpans(t *testing.T) {
35 var gotTraceparent []string
36 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
37 gotTraceparent = append(gotTraceparent, r.Header.Get("Traceparent"))
38 w.WriteHeader(http.StatusOK)
39 }))
40 t.Cleanup(upstream.Close)
41
42 t.Run("traced request", func(t *testing.T) {
43 gotTraceparent = nil
44 recorder := tracetest.NewSpanRecorder()
45 tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder))
46 t.Cleanup(func() { _ = tp.Shutdown(context.Background()) })
47
48 // round robin selects index 1 first, which refuses connections, so
49 // the retry produces a second client span under the same server span
50 h := minimalHandler(1,
51 &Upstream{Host: new(Host), Dial: upstream.Listener.Addr().String()},
52 &Upstream{Host: new(Host), Dial: deadUpstreamAddr(t)},
53 )
54 // HTTPTransport sets the URL scheme itself, so the span only records
55 // it if roundTrip calls SetScheme before the traced transport runs
56 caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
57 t.Cleanup(cancel)
58 ht := new(HTTPTransport)
59 if err := ht.Provision(caddyCtx); err != nil {
60 t.Fatalf("provisioning transport: %v", err)
61 }
62 h.Transport = ht
63 h.tracedTransport = newTracedTransport(h.Transport)
64
65 ctx, serverSpan := tp.Tracer("test").Start(context.Background(), "server",
66 trace.WithSpanKind(trace.SpanKindServer))
67 req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil).WithContext(ctx)
68 req = prepareTestRequest(req)
69 rec := httptest.NewRecorder()
70 if err := h.ServeHTTP(rec, req, nil); err != nil {
71 t.Fatalf("ServeHTTP: %v", err)
72 }
73 serverSpan.End()
74
75 var clientSpans []sdktrace.ReadOnlySpan
76 for _, s := range recorder.Ended() {
77 if s.SpanKind() == trace.SpanKindClient {
78 clientSpans = append(clientSpans, s)
79 }
80 }
81 if len(clientSpans) != 2 {
82 t.Fatalf("expected 2 client spans, got %d", len(clientSpans))
83 }
84 for i, s := range clientSpans {
85 if s.Parent().SpanID() != serverSpan.SpanContext().SpanID() {
86 t.Errorf("client span %d: parent is not the server span", i)
87 }
88 }
89 if clientSpans[0].Status().Code != codes.Error {
90 t.Errorf("expected failed attempt to have error status, got %v", clientSpans[0].Status().Code)
91 }
92
93 upstreamHost, upstreamPort, _ := net.SplitHostPort(upstream.Listener.Addr().String())
94 attrs := attribute.NewSet(clientSpans[1].Attributes()...)
95 for key, want := range map[attribute.Key]string{
96 "url.full": "http://" + upstream.Listener.Addr().String() + "/",
97 "server.address": upstreamHost,
98 "server.port": upstreamPort,
99 "http.response.status_code": "200",
100 } {
101 if got, _ := attrs.Value(key); got.Emit() != want {
102 t.Errorf("client span attribute %s: expected %q, got %q", key, want, got.Emit())
103 }
104 }
105
106 if len(gotTraceparent) != 1 {
107 t.Fatalf("expected 1 upstream request, got %d", len(gotTraceparent))
108 }
109 want := clientSpans[1].SpanContext().SpanID().String()
110 if !strings.Contains(gotTraceparent[0], want) {
111 t.Errorf("upstream traceparent %q does not reference client span %s", gotTraceparent[0], want)
112 }
113 })
114
115 t.Run("untraced request", func(t *testing.T) {
116 gotTraceparent = nil
117 h := minimalHandler(0, &Upstream{Host: new(Host), Dial: upstream.Listener.Addr().String()})
118 h.tracedTransport = newTracedTransport(h.Transport)
119
120 req := prepareTestRequest(httptest.NewRequest(http.MethodGet, "http://example.com/", nil))
121 if err := h.ServeHTTP(httptest.NewRecorder(), req, nil); err != nil {
122 t.Fatalf("ServeHTTP: %v", err)
123 }
124 if len(gotTraceparent) != 1 || gotTraceparent[0] != "" {
125 t.Errorf("expected no traceparent upstream, got %q", gotTraceparent)
126 }
127 })
128 }