Projet

Général

Profil

0002-utils-soap-ignore-content-before-and-after-SOAP-XML-.patch

Benjamin Dauvergne, 01 février 2021 12:04

Télécharger (4,67 ko)

Voir les différences:

Subject: [PATCH 2/3] utils/soap: ignore content before and after SOAP XML
 content (#50260)

 passerelle/utils/soap.py | 37 +++++++++++++++++++++++++++++++++++--
 tests/test_soap.py       | 32 +++++++++++++++++++++++++++++++-
 2 files changed, 66 insertions(+), 3 deletions(-)
passerelle/utils/soap.py
36 36
    """
37 37
    def __init__(self, resource, **kwargs):
38 38
        wsdl_url = kwargs.pop('wsdl_url', None) or resource.wsdl_url
39
        transport_kwargs = kwargs.pop('transport_kwargs', {})
39 40
        transport_class = getattr(resource, 'soap_transport_class', SOAPTransport)
40
        transport = transport_class(resource, wsdl_url, session=resource.requests, cache=InMemoryCache())
41
        transport = transport_class(resource, wsdl_url,
42
                                    session=resource.requests,
43
                                    cache=InMemoryCache(), **transport_kwargs)
41 44
        super(SOAPClient, self).__init__(wsdl_url, transport=transport, **kwargs)
42 45

  
43 46

  
47
class ResponseFixContentWrapper:
48
    def __init__(self, response):
49
        self.response = response
50

  
51
    def __getattr__(self, name):
52
        return getattr(self.response, name)
53

  
54
    @property
55
    def content(self):
56
        content = self.response.content
57
        if 'multipart/related' not in self.response.headers.get('Content-Type', ''):
58
            try:
59
                first_less_than_sign = content.index(b'<')
60
                last_greater_than_sign = content.rindex(b'>')
61
                content = content[first_less_than_sign:last_greater_than_sign + 1]
62
            except ValueError:
63
                pass
64
        return content
65

  
66

  
44 67
class SOAPTransport(Transport):
45 68
    """Wrapper around zeep.Transport
46 69

  
47 70
    disable basic_authentication hosts unrelated to wsdl's endpoints
48 71
    """
49
    def __init__(self, resource, wsdl_url, **kwargs):
72
    def __init__(self, resource, wsdl_url, remove_first_bytes_for_xml=False, **kwargs):
50 73
        self.resource = resource
51 74
        self.wsdl_host = urlparse.urlparse(wsdl_url).netloc
75
        # fix content for servers returning unexpected characters before XML document start
76
        self.remove_first_bytes_for_xml = remove_first_bytes_for_xml
52 77
        super(SOAPTransport, self).__init__(**kwargs)
53 78

  
54 79
    def _load_remote_data(self, url):
......
60 85
            return super(SOAPTransport, self)._load_remote_data(url)
61 86
        except RequestException as e:
62 87
            raise SOAPError('SOAP service is down, location %r cannot be loaded: %s' % (url, e), exception=e, url=url)
88

  
89
    def post_xml(self, *args, **kwargs):
90
        response = super().post_xml(*args, **kwargs)
91

  
92
        if self.remove_first_bytes_for_xml:
93
            return ResponseFixContentWrapper(response)
94

  
95
        return response
tests/test_soap.py
19 19
import requests
20 20
from zeep import Settings
21 21
from zeep.plugins import Plugin
22
from zeep.exceptions import XMLParseError
22
from zeep.exceptions import XMLParseError, TransportError
23 23

  
24 24
from passerelle.utils.soap import SOAPClient
25 25

  
......
78 78
    assert len(result) == 2
79 79
    assert result['skipMe'] is None
80 80
    assert result['price'] == 4.2
81

  
82

  
83
@mock.patch('requests.sessions.Session.post')
84
def test_remove_first_bytes_for_xml(mocked_post):
85
    response = requests.Response()
86
    response.status_code = 200
87
    response._content = force_bytes('''blabla \n<?xml version='1.0' encoding='utf-8'?>
88
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
89
  <soap-env:Body>
90
    <ns0:TradePrice xmlns:ns0="http://example.com/stockquote.xsd">
91
      <skipMe>1.2</skipMe>
92
      <price>4.20</price>
93
    </ns0:TradePrice>
94
  </soap-env:Body>
95
</soap-env:Envelope>\n bloublou''')
96
    mocked_post.return_value = response
97

  
98
    soap_resource = SOAPResource()
99

  
100
    client = SOAPClient(soap_resource)
101
    with pytest.raises(TransportError):
102
        client.service.GetLastTradePrice(tickerSymbol='banana')
103

  
104
    client = SOAPClient(soap_resource,
105
                        transport_kwargs={'remove_first_bytes_for_xml': True})
106
    result = client.service.GetLastTradePrice(tickerSymbol='banana')
107
    assert len(result) == 2
108
    assert result['skipMe'] == 1.2
109
    assert result['price'] == 4.2
110

  
81
-