Skip to content

Commit

Permalink
add back webapp2 support
Browse files Browse the repository at this point in the history
- add get_request_data_from_webapp2()
- adapt old tests to new patterns
- add back webapp2 3 beta dep to nox

removed in googleapis#78
  • Loading branch information
blech75 committed Jan 26, 2024
1 parent 6d265bf commit 04b849f
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 3 deletions.
49 changes: 48 additions & 1 deletion google/cloud/logging_v2/handlers/_helpers.py
Expand Up @@ -24,6 +24,11 @@
except ImportError: # pragma: NO COVER
flask = None

try:
import webapp2
except (ImportError, SyntaxError): # pragma: NO COVER
webapp2 = None

from google.cloud.logging_v2.handlers.middleware.request import _get_django_request

_DJANGO_CONTENT_LENGTH = "CONTENT_LENGTH"
Expand All @@ -34,6 +39,8 @@
_DJANGO_REFERER_HEADER = "HTTP_REFERER"
_FLASK_XCLOUD_TRACE_HEADER = "X_CLOUD_TRACE_CONTEXT"
_FLASK_TRACEPARENT = "TRACEPARENT"
_WEBAPP2_XCLOUD_TRACE_HEADER = "X-CLOUD-TRACE-CONTEXT"
_WEBAPP2_TRACEPARENT = "TRACEPARENT"
_PROTOCOL_HEADER = "SERVER_PROTOCOL"


Expand Down Expand Up @@ -66,7 +73,7 @@ def get_request_data_from_flask():
Returns:
Tuple[Optional[dict], Optional[str], Optional[str], bool]:
Data related to the current http request, trace_id, span_id and trace_sampled
for the request. All fields will be None if a django request isn't found.
for the request. All fields will be None if a flask request isn't found.
"""
if flask is None or not flask.request:
return None, None, None, False
Expand Down Expand Up @@ -131,6 +138,45 @@ def get_request_data_from_django():
return http_request, trace_id, span_id, trace_sampled


def get_request_data_from_webapp2():
"""Get http_request and trace data from webapp2 request headers.
Returns:
Tuple[Optional[dict], Optional[str], Optional[str], bool]:
Data related to the current http request, trace_id, span_id and trace_sampled
for the request. All fields will be None if a webapp2 request isn't found.
"""
if webapp2 is None:
return None, None, None, False

try:
# get_request() succeeds if we're in the middle of a webapp2
# request, or raises an assertion error otherwise:
# "Request global variable is not set".
req = webapp2.get_request()
except AssertionError:
return None, None, None, False

# build http_request
http_request = {
"requestMethod": req.method,
"requestUrl": req.url,
"userAgent": req.user_agent,
"protocol": req.http_version,
}

# find trace id and span id
# first check for w3c traceparent header
header = req.headers.get(_WEBAPP2_TRACEPARENT)
trace_id, span_id, trace_sampled = _parse_trace_parent(header)
if trace_id is None:
# traceparent not found. look for xcloud_trace_context header
header = req.headers.get(_WEBAPP2_XCLOUD_TRACE_HEADER)
trace_id, span_id, trace_sampled = _parse_xcloud_trace(header)

return http_request, trace_id, span_id, trace_sampled


def _parse_trace_parent(header):
"""Given a w3 traceparent header, extract the trace and span ids.
For more information see https://www.w3.org/TR/trace-context/
Expand Down Expand Up @@ -203,6 +249,7 @@ def get_request_data():
checkers = (
get_request_data_from_django,
get_request_data_from_flask,
get_request_data_from_webapp2,
)

for checker in checkers:
Expand Down
1 change: 1 addition & 0 deletions noxfile.py
Expand Up @@ -46,6 +46,7 @@
"flask",
"webob",
"django",
"webapp2==3.0.0b1",
]
UNIT_TEST_LOCAL_DEPENDENCIES: List[str] = []
UNIT_TEST_DEPENDENCIES: List[str] = []
Expand Down
4 changes: 2 additions & 2 deletions owlbot.py
Expand Up @@ -93,11 +93,11 @@ def place_before(path, text, *before_text, escape=None):
"google-cloud-storage",
"google-cloud-testutils",
],
unit_test_external_dependencies=["flask", "webob", "django"],
unit_test_external_dependencies=["flask", "webob", "django","webapp2==3.0.0b1"],
samples=True,
)

s.move(templated_files,
s.move(templated_files,
excludes=[
"docs/index.rst",
".github/release-please.yml",
Expand Down
111 changes: 111 additions & 0 deletions tests/unit/handlers/test__helpers.py
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import unittest

import mock
Expand All @@ -22,6 +23,9 @@
_DJANGO_TRACE_ID = "django0id"
_DJANGO_SPAN_ID = "span0django"
_DJANGO_HTTP_REQUEST = {"requestUrl": "https://www.djangoproject.com/"}
_WEBAPP2_TRACE_ID = "webapp20id"
_WEBAPP2_SPAN_ID = "span0webapp2"
_WEBAPP2_HTTP_REQUEST = {"requestUrl": "https://webapp2.readthedocs.io/en/latest/"}


class Test_get_request_data_from_flask(unittest.TestCase):
Expand Down Expand Up @@ -256,6 +260,113 @@ def test_invalid_host_header(self):
self.assertEqual(http_request["protocol"], "HTTP/1.1")


class Test_get_request_data_from_webapp2(unittest.TestCase):
@staticmethod
def create_app():
import webapp2

class TestHandler(webapp2.RequestHandler):
def get(self):
from google.cloud.logging_v2.handlers import _helpers

http, trace, span, sampled = _helpers.get_request_data_from_webapp2()

self.response.content_type = "application/json"
self.response.out.write(json.dumps([http, trace, span, sampled]))

app = webapp2.WSGIApplication([("/", TestHandler)], debug=True)

return app

def test_no_context_header(self):
import webob

req = webob.BaseRequest.blank("/")
response = req.get_response(self.create_app())

http_request, trace_id, span_id, sampled = json.loads(response.body)

self.assertEqual(None, trace_id)

def test_xcloud_header(self):
import webob

webapp2_trace_header = "X_CLOUD_TRACE_CONTEXT"
expected_trace_id = _WEBAPP2_TRACE_ID
expected_span_id = _WEBAPP2_SPAN_ID
webapp2_trace_id = f"{expected_trace_id}/{expected_span_id};o=1"

req = webob.BaseRequest.blank(
"/", headers={webapp2_trace_header: webapp2_trace_id}
)
response = req.get_response(self.create_app())

http_request, trace_id, span_id, sampled = json.loads(response.body)

self.assertEqual(trace_id, expected_trace_id)
self.assertEqual(span_id, expected_span_id)
self.assertEqual(sampled, True)
self.assertEqual(http_request["requestMethod"], "GET")

def test_traceparent_header(self):
import webob

webapp2_trace_header = "TRACEPARENT"
expected_trace_id = "4bf92f3577b34da6a3ce929d0e0e4736"
expected_span_id = "00f067aa0ba902b7"
webapp2_trace_id = f"00-{expected_trace_id}-{expected_span_id}-01"

req = webob.BaseRequest.blank(
"/", headers={webapp2_trace_header: webapp2_trace_id}
)
response = req.get_response(self.create_app())

http_request, trace_id, span_id, sampled = json.loads(response.body)

self.assertEqual(trace_id, expected_trace_id)
self.assertEqual(span_id, expected_span_id)
self.assertEqual(sampled, True)
self.assertEqual(http_request["requestMethod"], "GET")

def test_http_request_populated(self):
import webob

expected_path = "http://localhost/"
expected_agent = "Mozilla/5.0"
expected_referrer = "self"
expected_ip = "10.1.2.3"
headers = {
"User-Agent": expected_agent,
"Referer": expected_referrer,
}

req = webob.BaseRequest.blank(
'/', headers=headers, environ={"REMOTE_ADDR": expected_ip}
)
response = req.get_response(self.create_app())

http_request, *_ = json.loads(response.body)

self.assertEqual(http_request["requestMethod"], "GET")
self.assertEqual(http_request["requestUrl"], expected_path)
self.assertEqual(http_request["userAgent"], expected_agent)
self.assertEqual(http_request["protocol"], "HTTP/1.0")

def test_http_request_sparse(self):
import webob

expected_path = "http://localhost/"

req = webob.BaseRequest.blank("/")
response = req.get_response(self.create_app())

http_request, *_ = json.loads(response.body)

self.assertEqual(http_request["requestMethod"], "GET")
self.assertEqual(http_request["requestUrl"], expected_path)
self.assertEqual(http_request["protocol"], "HTTP/1.0")


class Test_get_request_data(unittest.TestCase):
@staticmethod
def _call_fut():
Expand Down

0 comments on commit 04b849f

Please sign in to comment.