From 4891440b7e39d246ce56b76374bcf4130e50b5f9 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 29 Jan 2021 16:02:09 +0100 Subject: [PATCH 2/2] misc: allow fixing prefix characters in SOAP responses (#50260) --- passerelle/utils/soap.py | 37 +++++++++++++++++++++++++++++++++++-- tests/test_soap.py | 32 +++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/passerelle/utils/soap.py b/passerelle/utils/soap.py index 6b28d942..d71fb34d 100644 --- a/passerelle/utils/soap.py +++ b/passerelle/utils/soap.py @@ -36,19 +36,43 @@ class SOAPClient(Client): """ def __init__(self, resource, **kwargs): wsdl_url = kwargs.pop('wsdl_url', None) or resource.wsdl_url + transport_kwargs = kwargs.pop('transport_kwargs', {}) transport_class = getattr(resource, 'soap_transport_class', SOAPTransport) - transport = transport_class(resource, wsdl_url, session=resource.requests, cache=InMemoryCache()) + transport = transport_class(resource, wsdl_url, + session=resource.requests, + cache=InMemoryCache(), **transport_kwargs) super(SOAPClient, self).__init__(wsdl_url, transport=transport, **kwargs) +class ResponseFixContentWrapper: + def __init__(self, response): + self.response = response + + def __getattr__(self, name): + return getattr(self.response, name) + + @property + def content(self): + content = self.response.content + if 'multipart/related' not in self.response.headers.get('Content-Type', ''): + try: + first_left_than_sign = content.index(b'<') + content = content[first_left_than_sign:] + except ValueError: + pass + return content + + class SOAPTransport(Transport): """Wrapper around zeep.Transport disable basic_authentication hosts unrelated to wsdl's endpoints """ - def __init__(self, resource, wsdl_url, **kwargs): + def __init__(self, resource, wsdl_url, remove_first_bytes_for_xml=False, **kwargs): self.resource = resource self.wsdl_host = urlparse.urlparse(wsdl_url).netloc + # fix content for servers returning unexpected characters before XML document start + self.remove_first_bytes_for_xml = remove_first_bytes_for_xml super(SOAPTransport, self).__init__(**kwargs) def _load_remote_data(self, url): @@ -60,3 +84,12 @@ class SOAPTransport(Transport): return super(SOAPTransport, self)._load_remote_data(url) except RequestException as e: raise SOAPError('SOAP service is down, location %r cannot be loaded: %s' % (url, e), exception=e, url=url) + + def post_xml(self, *args, **kwargs): + response = super().post_xml(*args, **kwargs) + + if self.remove_first_bytes_for_xml: + return ResponseFixContentWrapper(response) + + return response + diff --git a/tests/test_soap.py b/tests/test_soap.py index 72907eab..308bdc41 100644 --- a/tests/test_soap.py +++ b/tests/test_soap.py @@ -19,7 +19,7 @@ import mock import requests from zeep import Settings from zeep.plugins import Plugin -from zeep.exceptions import XMLParseError +from zeep.exceptions import XMLParseError, TransportError from passerelle.utils.soap import SOAPClient @@ -78,3 +78,33 @@ def test_disable_strict_mode(mocked_post): assert len(result) == 2 assert result['skipMe'] is None assert result['price'] == 4.2 + + +@mock.patch('requests.sessions.Session.post') +def test_remove_first_bytes_for_xml(mocked_post): + response = requests.Response() + response.status_code = 200 + response._content = force_bytes('''blabla + + + + 1.2 + 4.20 + + +''') + mocked_post.return_value = response + + soap_resource = SOAPResource() + + client = SOAPClient(soap_resource) + with pytest.raises(TransportError): + client.service.GetLastTradePrice(tickerSymbol='banana') + + client = SOAPClient(soap_resource, + transport_kwargs={'remove_first_bytes_for_xml': True}) + result = client.service.GetLastTradePrice(tickerSymbol='banana') + assert len(result) == 2 + assert result['skipMe'] == 1.2 + assert result['price'] == 4.2 + -- 2.29.2