|
31 | 31 | )
|
32 | 32 | from opentelemetry.test.test_base import TestBase
|
33 | 33 | from opentelemetry.trace import SpanKind, format_span_id, format_trace_id
|
| 34 | +from opentelemetry.util.http import ( |
| 35 | + OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, |
| 36 | + OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, |
| 37 | +) |
34 | 38 |
|
35 | 39 |
|
36 | 40 | async def http_app(scope, receive, send):
|
@@ -62,6 +66,47 @@ async def websocket_app(scope, receive, send):
|
62 | 66 | break
|
63 | 67 |
|
64 | 68 |
|
| 69 | +async def http_app_with_custom_headers(scope, receive, send): |
| 70 | + message = await receive() |
| 71 | + assert scope["type"] == "http" |
| 72 | + if message.get("type") == "http.request": |
| 73 | + await send( |
| 74 | + { |
| 75 | + "type": "http.response.start", |
| 76 | + "status": 200, |
| 77 | + "headers": [ |
| 78 | + (b"Content-Type", b"text/plain"), |
| 79 | + (b"custom-test-header-1", b"test-header-value-1"), |
| 80 | + (b"custom-test-header-2", b"test-header-value-2"), |
| 81 | + ], |
| 82 | + } |
| 83 | + ) |
| 84 | + await send({"type": "http.response.body", "body": b"*"}) |
| 85 | + |
| 86 | + |
| 87 | +async def websocket_app_with_custom_headers(scope, receive, send): |
| 88 | + assert scope["type"] == "websocket" |
| 89 | + while True: |
| 90 | + message = await receive() |
| 91 | + if message.get("type") == "websocket.connect": |
| 92 | + await send( |
| 93 | + { |
| 94 | + "type": "websocket.accept", |
| 95 | + "headers": [ |
| 96 | + (b"custom-test-header-1", b"test-header-value-1"), |
| 97 | + (b"custom-test-header-2", b"test-header-value-2"), |
| 98 | + ], |
| 99 | + } |
| 100 | + ) |
| 101 | + |
| 102 | + if message.get("type") == "websocket.receive": |
| 103 | + if message.get("text") == "ping": |
| 104 | + await send({"type": "websocket.send", "text": "pong"}) |
| 105 | + |
| 106 | + if message.get("type") == "websocket.disconnect": |
| 107 | + break |
| 108 | + |
| 109 | + |
65 | 110 | async def simple_asgi(scope, receive, send):
|
66 | 111 | assert isinstance(scope, dict)
|
67 | 112 | if scope["type"] == "http":
|
@@ -583,5 +628,237 @@ async def wrapped_app(scope, receive, send):
|
583 | 628 | )
|
584 | 629 |
|
585 | 630 |
|
| 631 | +@mock.patch.dict( |
| 632 | + "os.environ", |
| 633 | + { |
| 634 | + OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3", |
| 635 | + OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3", |
| 636 | + }, |
| 637 | +) |
| 638 | +class TestCustomHeaders(AsgiTestBase, TestBase): |
| 639 | + def setUp(self): |
| 640 | + super().setUp() |
| 641 | + self.tracer_provider, self.exporter = TestBase.create_tracer_provider() |
| 642 | + self.tracer = self.tracer_provider.get_tracer(__name__) |
| 643 | + self.app = otel_asgi.OpenTelemetryMiddleware( |
| 644 | + simple_asgi, tracer_provider=self.tracer_provider |
| 645 | + ) |
| 646 | + |
| 647 | + def test_http_custom_request_headers_in_span_attributes(self): |
| 648 | + self.scope["headers"].extend( |
| 649 | + [ |
| 650 | + (b"custom-test-header-1", b"test-header-value-1"), |
| 651 | + (b"custom-test-header-2", b"test-header-value-2"), |
| 652 | + ] |
| 653 | + ) |
| 654 | + self.seed_app(self.app) |
| 655 | + self.send_default_request() |
| 656 | + self.get_all_output() |
| 657 | + span_list = self.exporter.get_finished_spans() |
| 658 | + expected = { |
| 659 | + "http.request.header.custom_test_header_1": ( |
| 660 | + "test-header-value-1", |
| 661 | + ), |
| 662 | + "http.request.header.custom_test_header_2": ( |
| 663 | + "test-header-value-2", |
| 664 | + ), |
| 665 | + } |
| 666 | + for span in span_list: |
| 667 | + if span.kind == SpanKind.SERVER: |
| 668 | + self.assertSpanHasAttributes(span, expected) |
| 669 | + |
| 670 | + def test_http_custom_request_headers_not_in_span_attributes(self): |
| 671 | + self.scope["headers"].extend( |
| 672 | + [ |
| 673 | + (b"custom-test-header-1", b"test-header-value-1"), |
| 674 | + ] |
| 675 | + ) |
| 676 | + self.seed_app(self.app) |
| 677 | + self.send_default_request() |
| 678 | + self.get_all_output() |
| 679 | + span_list = self.exporter.get_finished_spans() |
| 680 | + expected = { |
| 681 | + "http.request.header.custom_test_header_1": ( |
| 682 | + "test-header-value-1", |
| 683 | + ), |
| 684 | + } |
| 685 | + not_expected = { |
| 686 | + "http.request.header.custom_test_header_2": ( |
| 687 | + "test-header-value-2", |
| 688 | + ), |
| 689 | + } |
| 690 | + for span in span_list: |
| 691 | + if span.kind == SpanKind.SERVER: |
| 692 | + self.assertSpanHasAttributes(span, expected) |
| 693 | + for key, _ in not_expected.items(): |
| 694 | + self.assertNotIn(key, span.attributes) |
| 695 | + |
| 696 | + def test_http_custom_response_headers_in_span_attributes(self): |
| 697 | + self.app = otel_asgi.OpenTelemetryMiddleware( |
| 698 | + http_app_with_custom_headers, tracer_provider=self.tracer_provider |
| 699 | + ) |
| 700 | + self.seed_app(self.app) |
| 701 | + self.send_default_request() |
| 702 | + self.get_all_output() |
| 703 | + span_list = self.exporter.get_finished_spans() |
| 704 | + expected = { |
| 705 | + "http.response.header.custom_test_header_1": ( |
| 706 | + "test-header-value-1", |
| 707 | + ), |
| 708 | + "http.response.header.custom_test_header_2": ( |
| 709 | + "test-header-value-2", |
| 710 | + ), |
| 711 | + } |
| 712 | + for span in span_list: |
| 713 | + if span.kind == SpanKind.SERVER: |
| 714 | + self.assertSpanHasAttributes(span, expected) |
| 715 | + |
| 716 | + def test_http_custom_response_headers_not_in_span_attributes(self): |
| 717 | + self.app = otel_asgi.OpenTelemetryMiddleware( |
| 718 | + http_app_with_custom_headers, tracer_provider=self.tracer_provider |
| 719 | + ) |
| 720 | + self.seed_app(self.app) |
| 721 | + self.send_default_request() |
| 722 | + self.get_all_output() |
| 723 | + span_list = self.exporter.get_finished_spans() |
| 724 | + not_expected = { |
| 725 | + "http.response.header.custom_test_header_3": ( |
| 726 | + "test-header-value-3", |
| 727 | + ), |
| 728 | + } |
| 729 | + for span in span_list: |
| 730 | + if span.kind == SpanKind.SERVER: |
| 731 | + for key, _ in not_expected.items(): |
| 732 | + self.assertNotIn(key, span.attributes) |
| 733 | + |
| 734 | + def test_websocket_custom_request_headers_in_span_attributes(self): |
| 735 | + self.scope = { |
| 736 | + "type": "websocket", |
| 737 | + "http_version": "1.1", |
| 738 | + "scheme": "ws", |
| 739 | + "path": "/", |
| 740 | + "query_string": b"", |
| 741 | + "headers": [ |
| 742 | + (b"custom-test-header-1", b"test-header-value-1"), |
| 743 | + (b"custom-test-header-2", b"test-header-value-2"), |
| 744 | + ], |
| 745 | + "client": ("127.0.0.1", 32767), |
| 746 | + "server": ("127.0.0.1", 80), |
| 747 | + } |
| 748 | + self.seed_app(self.app) |
| 749 | + self.send_input({"type": "websocket.connect"}) |
| 750 | + self.send_input({"type": "websocket.receive", "text": "ping"}) |
| 751 | + self.send_input({"type": "websocket.disconnect"}) |
| 752 | + |
| 753 | + self.get_all_output() |
| 754 | + span_list = self.exporter.get_finished_spans() |
| 755 | + expected = { |
| 756 | + "http.request.header.custom_test_header_1": ( |
| 757 | + "test-header-value-1", |
| 758 | + ), |
| 759 | + "http.request.header.custom_test_header_2": ( |
| 760 | + "test-header-value-2", |
| 761 | + ), |
| 762 | + } |
| 763 | + for span in span_list: |
| 764 | + if span.kind == SpanKind.SERVER: |
| 765 | + self.assertSpanHasAttributes(span, expected) |
| 766 | + |
| 767 | + def test_websocket_custom_request_headers_not_in_span_attributes(self): |
| 768 | + self.scope = { |
| 769 | + "type": "websocket", |
| 770 | + "http_version": "1.1", |
| 771 | + "scheme": "ws", |
| 772 | + "path": "/", |
| 773 | + "query_string": b"", |
| 774 | + "headers": [ |
| 775 | + (b"Custom-Test-Header-1", b"test-header-value-1"), |
| 776 | + (b"Custom-Test-Header-2", b"test-header-value-2"), |
| 777 | + ], |
| 778 | + "client": ("127.0.0.1", 32767), |
| 779 | + "server": ("127.0.0.1", 80), |
| 780 | + } |
| 781 | + self.seed_app(self.app) |
| 782 | + self.send_input({"type": "websocket.connect"}) |
| 783 | + self.send_input({"type": "websocket.receive", "text": "ping"}) |
| 784 | + self.send_input({"type": "websocket.disconnect"}) |
| 785 | + |
| 786 | + self.get_all_output() |
| 787 | + span_list = self.exporter.get_finished_spans() |
| 788 | + not_expected = { |
| 789 | + "http.request.header.custom_test_header_3": ( |
| 790 | + "test-header-value-3", |
| 791 | + ), |
| 792 | + } |
| 793 | + for span in span_list: |
| 794 | + if span.kind == SpanKind.SERVER: |
| 795 | + for key, _ in not_expected.items(): |
| 796 | + self.assertNotIn(key, span.attributes) |
| 797 | + |
| 798 | + def test_websocket_custom_response_headers_in_span_attributes(self): |
| 799 | + self.scope = { |
| 800 | + "type": "websocket", |
| 801 | + "http_version": "1.1", |
| 802 | + "scheme": "ws", |
| 803 | + "path": "/", |
| 804 | + "query_string": b"", |
| 805 | + "headers": [], |
| 806 | + "client": ("127.0.0.1", 32767), |
| 807 | + "server": ("127.0.0.1", 80), |
| 808 | + } |
| 809 | + self.app = otel_asgi.OpenTelemetryMiddleware( |
| 810 | + websocket_app_with_custom_headers, |
| 811 | + tracer_provider=self.tracer_provider, |
| 812 | + ) |
| 813 | + self.seed_app(self.app) |
| 814 | + self.send_input({"type": "websocket.connect"}) |
| 815 | + self.send_input({"type": "websocket.receive", "text": "ping"}) |
| 816 | + self.send_input({"type": "websocket.disconnect"}) |
| 817 | + self.get_all_output() |
| 818 | + span_list = self.exporter.get_finished_spans() |
| 819 | + expected = { |
| 820 | + "http.response.header.custom_test_header_1": ( |
| 821 | + "test-header-value-1", |
| 822 | + ), |
| 823 | + "http.response.header.custom_test_header_2": ( |
| 824 | + "test-header-value-2", |
| 825 | + ), |
| 826 | + } |
| 827 | + for span in span_list: |
| 828 | + if span.kind == SpanKind.SERVER: |
| 829 | + self.assertSpanHasAttributes(span, expected) |
| 830 | + |
| 831 | + def test_websocket_custom_response_headers_not_in_span_attributes(self): |
| 832 | + self.scope = { |
| 833 | + "type": "websocket", |
| 834 | + "http_version": "1.1", |
| 835 | + "scheme": "ws", |
| 836 | + "path": "/", |
| 837 | + "query_string": b"", |
| 838 | + "headers": [], |
| 839 | + "client": ("127.0.0.1", 32767), |
| 840 | + "server": ("127.0.0.1", 80), |
| 841 | + } |
| 842 | + self.app = otel_asgi.OpenTelemetryMiddleware( |
| 843 | + websocket_app_with_custom_headers, |
| 844 | + tracer_provider=self.tracer_provider, |
| 845 | + ) |
| 846 | + self.seed_app(self.app) |
| 847 | + self.send_input({"type": "websocket.connect"}) |
| 848 | + self.send_input({"type": "websocket.receive", "text": "ping"}) |
| 849 | + self.send_input({"type": "websocket.disconnect"}) |
| 850 | + self.get_all_output() |
| 851 | + span_list = self.exporter.get_finished_spans() |
| 852 | + not_expected = { |
| 853 | + "http.response.header.custom_test_header_3": ( |
| 854 | + "test-header-value-3", |
| 855 | + ), |
| 856 | + } |
| 857 | + for span in span_list: |
| 858 | + if span.kind == SpanKind.SERVER: |
| 859 | + for key, _ in not_expected.items(): |
| 860 | + self.assertNotIn(key, span.attributes) |
| 861 | + |
| 862 | + |
586 | 863 | if __name__ == "__main__":
|
587 | 864 | unittest.main()
|
0 commit comments