Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

543 linhas
14 KiB

  1. // Copyright 2018, OpenCensus 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. package ochttp
  15. import (
  16. "bytes"
  17. "context"
  18. "encoding/hex"
  19. "errors"
  20. "fmt"
  21. "io"
  22. "io/ioutil"
  23. "log"
  24. "net/http"
  25. "net/http/httptest"
  26. "reflect"
  27. "strings"
  28. "testing"
  29. "time"
  30. "go.opencensus.io/plugin/ochttp/propagation/b3"
  31. "go.opencensus.io/plugin/ochttp/propagation/tracecontext"
  32. "go.opencensus.io/trace"
  33. )
  34. type testExporter struct {
  35. spans []*trace.SpanData
  36. }
  37. func (t *testExporter) ExportSpan(s *trace.SpanData) {
  38. t.spans = append(t.spans, s)
  39. }
  40. type testTransport struct {
  41. ch chan *http.Request
  42. }
  43. func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  44. t.ch <- req
  45. return nil, errors.New("noop")
  46. }
  47. type testPropagator struct{}
  48. func (t testPropagator) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
  49. header := req.Header.Get("trace")
  50. buf, err := hex.DecodeString(header)
  51. if err != nil {
  52. log.Fatalf("Cannot decode trace header: %q", header)
  53. }
  54. r := bytes.NewReader(buf)
  55. r.Read(sc.TraceID[:])
  56. r.Read(sc.SpanID[:])
  57. opts, err := r.ReadByte()
  58. if err != nil {
  59. log.Fatalf("Cannot read trace options from trace header: %q", header)
  60. }
  61. sc.TraceOptions = trace.TraceOptions(opts)
  62. return sc, true
  63. }
  64. func (t testPropagator) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
  65. var buf bytes.Buffer
  66. buf.Write(sc.TraceID[:])
  67. buf.Write(sc.SpanID[:])
  68. buf.WriteByte(byte(sc.TraceOptions))
  69. req.Header.Set("trace", hex.EncodeToString(buf.Bytes()))
  70. }
  71. func TestTransport_RoundTrip_Race(t *testing.T) {
  72. // This tests that we don't modify the request in accordance with the
  73. // specification for http.RoundTripper.
  74. // We attempt to trigger a race by reading the request from a separate
  75. // goroutine. If the request is modified by Transport, this should trigger
  76. // the race detector.
  77. transport := &testTransport{ch: make(chan *http.Request, 1)}
  78. rt := &Transport{
  79. Propagation: &testPropagator{},
  80. Base: transport,
  81. }
  82. req, _ := http.NewRequest("GET", "http://foo.com", nil)
  83. go func() {
  84. fmt.Println(*req)
  85. }()
  86. rt.RoundTrip(req)
  87. _ = <-transport.ch
  88. }
  89. func TestTransport_RoundTrip(t *testing.T) {
  90. _, parent := trace.StartSpan(context.Background(), "parent")
  91. tests := []struct {
  92. name string
  93. parent *trace.Span
  94. }{
  95. {
  96. name: "no parent",
  97. parent: nil,
  98. },
  99. {
  100. name: "parent",
  101. parent: parent,
  102. },
  103. }
  104. for _, tt := range tests {
  105. t.Run(tt.name, func(t *testing.T) {
  106. transport := &testTransport{ch: make(chan *http.Request, 1)}
  107. rt := &Transport{
  108. Propagation: &testPropagator{},
  109. Base: transport,
  110. }
  111. req, _ := http.NewRequest("GET", "http://foo.com", nil)
  112. if tt.parent != nil {
  113. req = req.WithContext(trace.NewContext(req.Context(), tt.parent))
  114. }
  115. rt.RoundTrip(req)
  116. req = <-transport.ch
  117. span := trace.FromContext(req.Context())
  118. if header := req.Header.Get("trace"); header == "" {
  119. t.Fatalf("Trace header = empty; want valid trace header")
  120. }
  121. if span == nil {
  122. t.Fatalf("Got no spans in req context; want one")
  123. }
  124. if tt.parent != nil {
  125. if got, want := span.SpanContext().TraceID, tt.parent.SpanContext().TraceID; got != want {
  126. t.Errorf("span.SpanContext().TraceID=%v; want %v", got, want)
  127. }
  128. }
  129. })
  130. }
  131. }
  132. func TestHandler(t *testing.T) {
  133. traceID := [16]byte{16, 84, 69, 170, 120, 67, 188, 139, 242, 6, 177, 32, 0, 16, 0, 0}
  134. tests := []struct {
  135. header string
  136. wantTraceID trace.TraceID
  137. wantTraceOptions trace.TraceOptions
  138. }{
  139. {
  140. header: "105445aa7843bc8bf206b12000100000000000000000000000",
  141. wantTraceID: traceID,
  142. wantTraceOptions: trace.TraceOptions(0),
  143. },
  144. {
  145. header: "105445aa7843bc8bf206b12000100000000000000000000001",
  146. wantTraceID: traceID,
  147. wantTraceOptions: trace.TraceOptions(1),
  148. },
  149. }
  150. for _, tt := range tests {
  151. t.Run(tt.header, func(t *testing.T) {
  152. handler := &Handler{
  153. Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  154. span := trace.FromContext(r.Context())
  155. sc := span.SpanContext()
  156. if got, want := sc.TraceID, tt.wantTraceID; got != want {
  157. t.Errorf("TraceID = %q; want %q", got, want)
  158. }
  159. if got, want := sc.TraceOptions, tt.wantTraceOptions; got != want {
  160. t.Errorf("TraceOptions = %v; want %v", got, want)
  161. }
  162. }),
  163. StartOptions: trace.StartOptions{Sampler: trace.ProbabilitySampler(0.0)},
  164. Propagation: &testPropagator{},
  165. }
  166. req, _ := http.NewRequest("GET", "http://foo.com", nil)
  167. req.Header.Add("trace", tt.header)
  168. handler.ServeHTTP(nil, req)
  169. })
  170. }
  171. }
  172. var _ http.RoundTripper = (*traceTransport)(nil)
  173. type collector []*trace.SpanData
  174. func (c *collector) ExportSpan(s *trace.SpanData) {
  175. *c = append(*c, s)
  176. }
  177. func TestEndToEnd(t *testing.T) {
  178. tc := []struct {
  179. name string
  180. handler *Handler
  181. transport *Transport
  182. wantSameTraceID bool
  183. wantLinks bool // expect a link between client and server span
  184. }{
  185. {
  186. name: "internal default propagation",
  187. handler: &Handler{},
  188. transport: &Transport{},
  189. wantSameTraceID: true,
  190. },
  191. {
  192. name: "external default propagation",
  193. handler: &Handler{IsPublicEndpoint: true},
  194. transport: &Transport{},
  195. wantSameTraceID: false,
  196. wantLinks: true,
  197. },
  198. {
  199. name: "internal TraceContext propagation",
  200. handler: &Handler{Propagation: &tracecontext.HTTPFormat{}},
  201. transport: &Transport{Propagation: &tracecontext.HTTPFormat{}},
  202. wantSameTraceID: true,
  203. },
  204. {
  205. name: "misconfigured propagation",
  206. handler: &Handler{IsPublicEndpoint: true, Propagation: &tracecontext.HTTPFormat{}},
  207. transport: &Transport{Propagation: &b3.HTTPFormat{}},
  208. wantSameTraceID: false,
  209. wantLinks: false,
  210. },
  211. }
  212. for _, tt := range tc {
  213. t.Run(tt.name, func(t *testing.T) {
  214. var spans collector
  215. trace.RegisterExporter(&spans)
  216. defer trace.UnregisterExporter(&spans)
  217. // Start the server.
  218. serverDone := make(chan struct{})
  219. serverReturn := make(chan time.Time)
  220. tt.handler.StartOptions.Sampler = trace.AlwaysSample()
  221. url := serveHTTP(tt.handler, serverDone, serverReturn)
  222. ctx := context.Background()
  223. // Make the request.
  224. req, err := http.NewRequest(
  225. http.MethodPost,
  226. fmt.Sprintf("%s/example/url/path?qparam=val", url),
  227. strings.NewReader("expected-request-body"))
  228. if err != nil {
  229. t.Fatal(err)
  230. }
  231. req = req.WithContext(ctx)
  232. tt.transport.StartOptions.Sampler = trace.AlwaysSample()
  233. c := &http.Client{
  234. Transport: tt.transport,
  235. }
  236. resp, err := c.Do(req)
  237. if err != nil {
  238. t.Fatal(err)
  239. }
  240. if resp.StatusCode != http.StatusOK {
  241. t.Fatalf("resp.StatusCode = %d", resp.StatusCode)
  242. }
  243. // Tell the server to return from request handling.
  244. serverReturn <- time.Now().Add(time.Millisecond)
  245. respBody, err := ioutil.ReadAll(resp.Body)
  246. if err != nil {
  247. t.Fatal(err)
  248. }
  249. if got, want := string(respBody), "expected-response"; got != want {
  250. t.Fatalf("respBody = %q; want %q", got, want)
  251. }
  252. resp.Body.Close()
  253. <-serverDone
  254. trace.UnregisterExporter(&spans)
  255. if got, want := len(spans), 2; got != want {
  256. t.Fatalf("len(spans) = %d; want %d", got, want)
  257. }
  258. var client, server *trace.SpanData
  259. for _, sp := range spans {
  260. switch sp.SpanKind {
  261. case trace.SpanKindClient:
  262. client = sp
  263. if got, want := client.Name, "/example/url/path"; got != want {
  264. t.Errorf("Span name: %q; want %q", got, want)
  265. }
  266. case trace.SpanKindServer:
  267. server = sp
  268. if got, want := server.Name, "/example/url/path"; got != want {
  269. t.Errorf("Span name: %q; want %q", got, want)
  270. }
  271. default:
  272. t.Fatalf("server or client span missing; kind = %v", sp.SpanKind)
  273. }
  274. }
  275. if tt.wantSameTraceID {
  276. if server.TraceID != client.TraceID {
  277. t.Errorf("TraceID does not match: server.TraceID=%q client.TraceID=%q", server.TraceID, client.TraceID)
  278. }
  279. if !server.HasRemoteParent {
  280. t.Errorf("server span should have remote parent")
  281. }
  282. if server.ParentSpanID != client.SpanID {
  283. t.Errorf("server span should have client span as parent")
  284. }
  285. }
  286. if !tt.wantSameTraceID {
  287. if server.TraceID == client.TraceID {
  288. t.Errorf("TraceID should not be trusted")
  289. }
  290. }
  291. if tt.wantLinks {
  292. if got, want := len(server.Links), 1; got != want {
  293. t.Errorf("len(server.Links) = %d; want %d", got, want)
  294. } else {
  295. link := server.Links[0]
  296. if got, want := link.Type, trace.LinkTypeParent; got != want {
  297. t.Errorf("link.Type = %v; want %v", got, want)
  298. }
  299. }
  300. }
  301. if server.StartTime.Before(client.StartTime) {
  302. t.Errorf("server span starts before client span")
  303. }
  304. if server.EndTime.After(client.EndTime) {
  305. t.Errorf("client span ends before server span")
  306. }
  307. })
  308. }
  309. }
  310. func serveHTTP(handler *Handler, done chan struct{}, wait chan time.Time) string {
  311. handler.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  312. w.WriteHeader(200)
  313. w.(http.Flusher).Flush()
  314. // Simulate a slow-responding server.
  315. sleepUntil := <-wait
  316. for time.Now().Before(sleepUntil) {
  317. time.Sleep(sleepUntil.Sub(time.Now()))
  318. }
  319. io.WriteString(w, "expected-response")
  320. close(done)
  321. })
  322. server := httptest.NewServer(handler)
  323. go func() {
  324. <-done
  325. server.Close()
  326. }()
  327. return server.URL
  328. }
  329. func TestSpanNameFromURL(t *testing.T) {
  330. tests := []struct {
  331. u string
  332. want string
  333. }{
  334. {
  335. u: "http://localhost:80/hello?q=a",
  336. want: "/hello",
  337. },
  338. {
  339. u: "/a/b?q=c",
  340. want: "/a/b",
  341. },
  342. }
  343. for _, tt := range tests {
  344. t.Run(tt.u, func(t *testing.T) {
  345. req, err := http.NewRequest("GET", tt.u, nil)
  346. if err != nil {
  347. t.Errorf("url issue = %v", err)
  348. }
  349. if got := spanNameFromURL(req); got != tt.want {
  350. t.Errorf("spanNameFromURL() = %v, want %v", got, tt.want)
  351. }
  352. })
  353. }
  354. }
  355. func TestFormatSpanName(t *testing.T) {
  356. formatSpanName := func(r *http.Request) string {
  357. return r.Method + " " + r.URL.Path
  358. }
  359. handler := &Handler{
  360. Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
  361. resp.Write([]byte("Hello, world!"))
  362. }),
  363. FormatSpanName: formatSpanName,
  364. }
  365. server := httptest.NewServer(handler)
  366. defer server.Close()
  367. client := &http.Client{
  368. Transport: &Transport{
  369. FormatSpanName: formatSpanName,
  370. StartOptions: trace.StartOptions{
  371. Sampler: trace.AlwaysSample(),
  372. },
  373. },
  374. }
  375. tests := []struct {
  376. u string
  377. want string
  378. }{
  379. {
  380. u: "/hello?q=a",
  381. want: "GET /hello",
  382. },
  383. {
  384. u: "/a/b?q=c",
  385. want: "GET /a/b",
  386. },
  387. }
  388. for _, tt := range tests {
  389. t.Run(tt.u, func(t *testing.T) {
  390. var te testExporter
  391. trace.RegisterExporter(&te)
  392. res, err := client.Get(server.URL + tt.u)
  393. if err != nil {
  394. t.Fatalf("error creating request: %v", err)
  395. }
  396. res.Body.Close()
  397. trace.UnregisterExporter(&te)
  398. if want, got := 2, len(te.spans); want != got {
  399. t.Fatalf("got exported spans %#v, wanted two spans", te.spans)
  400. }
  401. if got := te.spans[0].Name; got != tt.want {
  402. t.Errorf("spanNameFromURL() = %v, want %v", got, tt.want)
  403. }
  404. if got := te.spans[1].Name; got != tt.want {
  405. t.Errorf("spanNameFromURL() = %v, want %v", got, tt.want)
  406. }
  407. })
  408. }
  409. }
  410. func TestRequestAttributes(t *testing.T) {
  411. tests := []struct {
  412. name string
  413. makeReq func() *http.Request
  414. wantAttrs []trace.Attribute
  415. }{
  416. {
  417. name: "GET example.com/hello",
  418. makeReq: func() *http.Request {
  419. req, _ := http.NewRequest("GET", "http://example.com:779/hello", nil)
  420. req.Header.Add("User-Agent", "ua")
  421. return req
  422. },
  423. wantAttrs: []trace.Attribute{
  424. trace.StringAttribute("http.path", "/hello"),
  425. trace.StringAttribute("http.host", "example.com:779"),
  426. trace.StringAttribute("http.method", "GET"),
  427. trace.StringAttribute("http.user_agent", "ua"),
  428. },
  429. },
  430. }
  431. for _, tt := range tests {
  432. t.Run(tt.name, func(t *testing.T) {
  433. req := tt.makeReq()
  434. attrs := requestAttrs(req)
  435. if got, want := attrs, tt.wantAttrs; !reflect.DeepEqual(got, want) {
  436. t.Errorf("Request attributes = %#v; want %#v", got, want)
  437. }
  438. })
  439. }
  440. }
  441. func TestResponseAttributes(t *testing.T) {
  442. tests := []struct {
  443. name string
  444. resp *http.Response
  445. wantAttrs []trace.Attribute
  446. }{
  447. {
  448. name: "non-zero HTTP 200 response",
  449. resp: &http.Response{StatusCode: 200},
  450. wantAttrs: []trace.Attribute{
  451. trace.Int64Attribute("http.status_code", 200),
  452. },
  453. },
  454. {
  455. name: "zero HTTP 500 response",
  456. resp: &http.Response{StatusCode: 500},
  457. wantAttrs: []trace.Attribute{
  458. trace.Int64Attribute("http.status_code", 500),
  459. },
  460. },
  461. }
  462. for _, tt := range tests {
  463. t.Run(tt.name, func(t *testing.T) {
  464. attrs := responseAttrs(tt.resp)
  465. if got, want := attrs, tt.wantAttrs; !reflect.DeepEqual(got, want) {
  466. t.Errorf("Response attributes = %#v; want %#v", got, want)
  467. }
  468. })
  469. }
  470. }
  471. func TestStatusUnitTest(t *testing.T) {
  472. tests := []struct {
  473. in int
  474. want trace.Status
  475. }{
  476. {200, trace.Status{Code: trace.StatusCodeOK, Message: `OK`}},
  477. {204, trace.Status{Code: trace.StatusCodeOK, Message: `OK`}},
  478. {100, trace.Status{Code: trace.StatusCodeUnknown, Message: `UNKNOWN`}},
  479. {500, trace.Status{Code: trace.StatusCodeUnknown, Message: `UNKNOWN`}},
  480. {404, trace.Status{Code: trace.StatusCodeNotFound, Message: `NOT_FOUND`}},
  481. {600, trace.Status{Code: trace.StatusCodeUnknown, Message: `UNKNOWN`}},
  482. {401, trace.Status{Code: trace.StatusCodeUnauthenticated, Message: `UNAUTHENTICATED`}},
  483. {403, trace.Status{Code: trace.StatusCodePermissionDenied, Message: `PERMISSION_DENIED`}},
  484. {301, trace.Status{Code: trace.StatusCodeOK, Message: `OK`}},
  485. {501, trace.Status{Code: trace.StatusCodeUnimplemented, Message: `UNIMPLEMENTED`}},
  486. }
  487. for _, tt := range tests {
  488. got, want := TraceStatus(tt.in, ""), tt.want
  489. if got != want {
  490. t.Errorf("status(%d) got = (%#v) want = (%#v)", tt.in, got, want)
  491. }
  492. }
  493. }