Skip to content

Commit

Permalink
Urllib3: extend request hook with request body and headers
Browse files Browse the repository at this point in the history
  • Loading branch information
ItayGibel-helios committed Sep 2, 2021
1 parent c4639ee commit 0f7ae6b
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ def response_hook(span, request, response):
_RequestHookT = typing.Optional[
typing.Callable[[Span, urllib3.connectionpool.HTTPConnectionPool], None]
]
_ExtendedRequestHookT = typing.Optional[
typing.Callable[
[
Span,
urllib3.connectionpool.HTTPConnectionPool,
# Request headers dict
typing.Dict,
# Request Body
str,
],
None]
]
_ResponseHookT = typing.Optional[
typing.Callable[
[
Expand Down Expand Up @@ -139,7 +151,7 @@ def _uninstrument(self, **kwargs):

def _instrument(
tracer,
request_hook: _RequestHookT = None,
request_hook: typing.Union[_RequestHookT, _ExtendedRequestHookT] = None,
response_hook: _ResponseHookT = None,
url_filter: _UrlFilterT = None,
):
Expand All @@ -150,6 +162,7 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
method = _get_url_open_arg("method", args, kwargs).upper()
url = _get_url(instance, args, kwargs, url_filter)
headers = _prepare_headers(kwargs)
body = _get_url_open_arg("body", args, kwargs)

span_name = "HTTP {}".format(method.strip())
span_attributes = {
Expand All @@ -161,7 +174,7 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
span_name, kind=SpanKind.CLIENT, attributes=span_attributes
) as span:
if callable(request_hook):
request_hook(span, instance)
_call_request_hook(request_hook, span, instance, headers, body)
inject(headers)

with _suppress_further_instrumentation():
Expand All @@ -179,6 +192,19 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
)


def _call_request_hook(request_hook: typing.Union[_RequestHookT, _ExtendedRequestHookT],
span: Span,
connection_pool: urllib3.connectionpool.HTTPConnectionPool,
headers: typing.Dict,
body: str):
try:
# First assume request_hook is a function of type _ExtendedRequestHookT
request_hook(span, connection_pool, headers, body)
except TypeError:
# Fallback to call request_hook as a function of type _RequestHookT
request_hook(span, connection_pool)


def _get_url_open_arg(name: str, args: typing.List, kwargs: typing.Mapping):
arg_idx = _URL_OPEN_ARG_TO_INDEX_MAPPING.get(name)
if arg_idx is not None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import typing
from unittest import mock

Expand Down Expand Up @@ -279,3 +279,28 @@ def response_hook(span, request, response):
self.assertEqual(span.name, "name set from hook")
self.assertIn("response_hook_attr", span.attributes)
self.assertEqual(span.attributes["response_hook_attr"], "value")

def test_extended_request_hook(self):
def extended_request_hook(span, request, headers, body):
span.set_attribute("request_hook_headers", json.dumps(headers))
span.set_attribute("request_hook_body", body)

URLLib3Instrumentor().uninstrument()
URLLib3Instrumentor().instrument(
request_hook=extended_request_hook,
)

headers = {"header1": "value1", "header2": "value2"}
body = "param1=1&param2=2"

pool = urllib3.HTTPConnectionPool("httpbin.org")
response = pool.request("GET", "/status/200", body=body, headers=headers)

self.assertEqual(b"Hello!", response.data)

span = self.assert_span()

self.assertIn("request_hook_headers", span.attributes)
self.assertEqual(span.attributes["request_hook_headers"], json.dumps(headers))
self.assertIn("request_hook_body", span.attributes)
self.assertEqual(span.attributes["request_hook_body"], body)

0 comments on commit 0f7ae6b

Please sign in to comment.