Skip to content

Commit

Permalink
geventhttpclient does not support cookies in request (#288)
Browse files Browse the repository at this point in the history
* geventhttpclient does not support `cookies` in `request`

add the dict (name, value) cookies in the clients `cookiejar` as
a `Cookie` instead.

* dict is not a decimal...
  • Loading branch information
mgor authored Nov 27, 2023
1 parent f0c635c commit 0295702
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 14 deletions.
2 changes: 1 addition & 1 deletion grizzly/locust.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ def watch_running_users() -> None:
gevent.sleep(1.0)
count += 1
if count % 10 == 0:
logger.debug('runner.user_count=%d, runner.user_classes_count=%d', runner.user_count, runner.user_classes_count)
logger.debug('runner.user_count=%d, runner.user_classes_count=%r', runner.user_count, runner.user_classes_count)
count = 0

logger.info('runner.user_count=%d, quit %s, abort_test=%r', runner.user_count, runner.__class__.__name__, abort_test)
Expand Down
2 changes: 1 addition & 1 deletion grizzly/users/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def request(self, request: RequestTask) -> GrizzlyResponse:

metadata, payload = request_impl(request)
except Exception as e:
message = f'request failed: {str(e) or e.__class__}'
message = f'request "{request.name}" failed: {str(e) or e.__class__}'
self.logger.exception(message)
exception = e
finally:
Expand Down
60 changes: 58 additions & 2 deletions grizzly/users/restapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@
import json
from abc import ABCMeta
from copy import copy
from html.parser import HTMLParser
from http.cookiejar import Cookie
from time import time
from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, cast
from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Tuple, cast

import requests
from locust.contrib.fasthttp import FastHttpSession
Expand All @@ -78,6 +80,32 @@
class RestApiUserMeta(GrizzlyUserMeta, ABCMeta):
pass

class HtmlTitleParser(HTMLParser):
title: Optional[str]

_look: bool

def __init__(self) -> None:
super().__init__()

self.title = None
self._look = False

@property
def should_look(self) -> bool:
return self._look and self.title is None

def handle_starttag(self, tag: str, _attrs: List[Tuple[str, Optional[str]]]) -> None:
self._look = tag == 'title' and self.title is None

def handle_data(self, data: str) -> None:
if self.should_look:
self.title = data

def handle_endtag(self, tag: str) -> None:
if tag == 'title' and self.should_look:
self._look = False


@grizzlycontext(context={
'verify_certificates': True,
Expand Down Expand Up @@ -153,6 +181,12 @@ def _get_error_message(self, response: FastResponseContextManager) -> str:
except json.decoder.JSONDecodeError:
message = response.text

parser = HtmlTitleParser()
parser.feed(message)

if parser.title is not None:
message = parser.title.strip()

return message

def async_request_impl(self, request: RequestTask) -> GrizzlyResponse:
Expand Down Expand Up @@ -209,15 +243,37 @@ def _request(self, request: RequestTask, client: FastHttpSession) -> GrizzlyResp
else:
parameters['data'] = request.source.encode('utf-8')

# from response...
headers: Optional[Dict[str, str]] = None
payload: Optional[str] = None

client.cookiejar.clear()

for name, value in self.cookies.items():
client.cookiejar.set_cookie(Cookie(
version=0,
name=name,
value=value,
port=None,
port_specified=False,
domain=self.host,
domain_specified=True,
domain_initial_dot=False,
path='/',
path_specified=True,
secure=self.host.startswith('https'),
expires=None,
discard=False,
comment=None,
comment_url=None,
rest={},
))

with client.request(
method=request.method.name,
name=request.name,
url=url,
catch_response=True,
cookies=self.cookies,
**parameters,
) as response:
# monkey patch, so we don't get two request events
Expand Down
5 changes: 5 additions & 0 deletions grizzly_extras/async_message/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ def configure_logging() -> None:
handlers=handlers,
)

# silence library loggers
for logger_name in ['azure']:
logger = logging.getLogger(logger_name)
logger.setLevel(logging.ERROR)

configure_logging()


Expand Down
21 changes: 11 additions & 10 deletions tests/unit/test_grizzly/users/test_restapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,9 @@ def test_get_oauth_authorization_real(self, grizzly_fixture: GrizzlyFixture, moc
},
'verify_certificates': False,
'metadata': {
'Ocp-Apim-Subscription-Key': '',
'User-Agent': '',
},
}
parent.user.metadata.update({
'Ocp-Apim-Subscription-Key': '',
})
parent.user.host = cast(dict, parent.user.__context__)['host']
parent.user.session_started = time()

Expand Down Expand Up @@ -156,6 +153,16 @@ def test_get_error_message(self, grizzly_fixture: GrizzlyFixture, mocker: Mocker
response_context_manager = create_mocked_fast_response_context_manager(content='{"success": false}', status_code=999)
assert parent.user._get_error_message(response_context_manager) == '{"success": false}'

response_context_manager = create_mocked_fast_response_context_manager(content="""<html>
<head>
<title> what a bummer </title>
</head>
<body>
<h1>meep</h1>
</body>
</html>""", status_code=999)
assert parent.user._get_error_message(response_context_manager) == 'what a bummer'

text_mock = mocker.patch('locust.contrib.fasthttp.FastResponse.text', new_callable=mocker.PropertyMock)
text_mock.return_value = None
assert parent.user._get_error_message(response_context_manager) == "unknown response <class 'locust.contrib.fasthttp.ResponseContextManager'>"
Expand Down Expand Up @@ -321,7 +328,6 @@ def test__request( # noqa: PLR0915
name='001 TestScenario',
url='http://test/api/test',
catch_response=True,
cookies=parent.user.cookies,
**expected_parameters,
)

Expand All @@ -346,7 +352,6 @@ def test__request( # noqa: PLR0915
name='001 TestScenario',
url='http://test/api/test',
catch_response=True,
cookies=parent.user.cookies,
**expected_parameters,
)

Expand All @@ -368,7 +373,6 @@ def test__request( # noqa: PLR0915
name='001 TestScenario',
url='http://test/api/test',
catch_response=True,
cookies=parent.user.cookies,
**expected_parameters,
)

Expand All @@ -393,7 +397,6 @@ def test__request( # noqa: PLR0915
name='001 TestScenario',
url='http://test/api/test',
catch_response=True,
cookies=parent.user.cookies,
**expected_parameters,
)

Expand Down Expand Up @@ -432,7 +435,6 @@ def test__request( # noqa: PLR0915
name='001 TestScenario',
url='http://test/api/test',
catch_response=True,
cookies=parent.user.cookies,
**expected_parameters,
)

Expand All @@ -453,7 +455,6 @@ def test__request( # noqa: PLR0915
name='001 TestScenario',
url='http://test/api/test',
catch_response=True,
cookies=parent.user.cookies,
**expected_parameters,
)

Expand Down
16 changes: 16 additions & 0 deletions tests/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
@app.route('/api/v1/resources/dogs')
def app_get_dog_fact() -> FlaskResponse:
logger.debug('route /api/v1/resources/dogs called')

if len(request.get_data(cache=False, as_text=True)) > 0:
return FlaskResponse(status=403)

_ = int(request.args.get('number', ''))

return jsonify(['woof woof wooof'])
Expand All @@ -46,6 +50,10 @@ def app_get_dog_fact() -> FlaskResponse:
@app.route('/facts')
def app_get_cat_fact() -> FlaskResponse:
logger.debug('route /facts called')

if len(request.get_data(cache=False, as_text=True)) > 0:
return FlaskResponse(status=403)

_ = int(request.args.get('limit', ''))

return jsonify(['meow meow meow'])
Expand All @@ -54,6 +62,10 @@ def app_get_cat_fact() -> FlaskResponse:
@app.route('/books/<book>.json')
def app_get_book(book: str) -> FlaskResponse:
logger.debug('/books/%s.json called, root_dir=%s', book, root_dir)

if len(request.get_data(cache=False, as_text=True)) > 0:
return FlaskResponse(status=403)

with (root_dir / 'requests' / 'books' / 'books.csv').open() as fd:
reader = csv.DictReader(fd)
for row in reader:
Expand All @@ -77,6 +89,10 @@ def app_get_author(author_key: str) -> FlaskResponse:
logger.debug('route /author/%s.json called', author_key)
name, _ = author_key.rsplit('|', 1)

if len(request.get_data(cache=False, as_text=True)) > 0:
return FlaskResponse(status=403)


return jsonify({
'name': name.replace('_', ' '),
})
Expand Down

0 comments on commit 0295702

Please sign in to comment.