pythonsoapproxysoap-clientzeep

Changing the service URL when accessing SOAP via a proxy using Zeep


Within my application, I need to access an internal (corporate) Soap API. For this access I have used Zeep so far. But now the access has to go through a proxy and the actual address of the API has to be converted to a virtual address of the proxy.

Creating the Zeep client also works correctly and I can access the WSDL files. However, the problem occurs when calling the respective service, because Zeep takes the corresponding URL from the WSDL file and this does not match the virtual address of the proxy.

I'll try to illustrate the problem below with my concrete code:

Assuming the address of the SOAP API is https://original-soap/wsdl?service=<service_name>.

In the proxy there is a mapping from https://origial-soap to http://virtual-soap.

So the address Zeep should use then is http://virtual-soap/wsdl?service=<service_name>.

I initialize my Zeep client as follows:

from requests.auth import HTTPBasicAuth
from requests import Session
from zeep import Client
from zeep.transports import Transport
from zeep.helpers import serialize_object

session = Session()
session.proxies = {
    'http': 'http://<proxy_host>:<proxy_port>',
    'https': 'http://<proxy_host>:<proxy_port>',
}
proxy_header = {
    "Proxy-Authorization": "Bearer <proxy_access_token>"
}
session.headers.update(proxy_header)
session.verify = False
session.auth = HTTPBasicAuth(<soap_user>, <soap_password>)
transport = Transport(session=session)

client = Client(wsdl='http://virtual-saop/wsdl?service=<service_name>', transport=transport)

print('CLIENT INITIALIZED') # <-- This print command is executed

soap_result = client.service['<service_function_name>'](<function parameters>) # <-- Connectivity errors occur here

So my question is how I can also change the URL that Zeep uses when calling the service so that the virtual address is used here as well.

Thanks for any help in advance!

Thanks to @Bogdan, I solved the problem using the following code for the service initialization:

service = client.create_service(
    client.service._binding.name, client.service._binding_options['address'].replace('https://original-soap:443', 'http://virtual-soap:80', 1)
)

Solution

  • There is another way to create the ServiceProxy that's supposed to do that.

    See the documentation at https://docs.python-zeep.org/en/master/client.html#creating-new-serviceproxy-objects

    The default ServiceProxy instance is created using the address in the WSDL, but the above way of creating the ServiceProxy allows more control on the address.

    The value for {http://my-target-namespace-here}myBinding is your binding identifier. If you do a python -mzeep https://original-soap/wsdl you should get an output like this one (I'm using some online example here for demonstration purposes, since I don't have access to your WSDL):

    > python -mzeep http://www.dneonline.com/calculator.asmx?WSDL
    
        
    Prefixes:
         xsd: http://www.w3.org/2001/XMLSchema
         ns0: http://tempuri.org/
    
    Global elements:
         ns0:Add(intA: xsd:int, intB: xsd:int)
         ns0:AddResponse(AddResult: xsd:int)
         ns0:Divide(intA: xsd:int, intB: xsd:int)
         ns0:DivideResponse(DivideResult: xsd:int)
         ns0:Multiply(intA: xsd:int, intB: xsd:int)
         ns0:MultiplyResponse(MultiplyResult: xsd:int)
         ns0:Subtract(intA: xsd:int, intB: xsd:int)
         ns0:SubtractResponse(SubtractResult: xsd:int)
         
    
    Global types:
         xsd:anyType
         xsd:ENTITIES
         xsd:ENTITY
         xsd:ID
         xsd:IDREF
         xsd:IDREFS
         xsd:NCName
         xsd:NMTOKEN
         xsd:NMTOKENS
         xsd:NOTATION
         xsd:Name
         xsd:QName
         xsd:anySimpleType
         xsd:anyURI
         xsd:base64Binary
         xsd:boolean
         xsd:byte
         xsd:date
         xsd:dateTime
         xsd:decimal
         xsd:double
         xsd:duration
         xsd:float
         xsd:gDay
         xsd:gMonth
         xsd:gMonthDay
         xsd:gYear
         xsd:gYearMonth
         xsd:hexBinary
         xsd:int
         xsd:integer
         xsd:language
         xsd:long
         xsd:negativeInteger
         xsd:nonNegativeInteger
         xsd:nonPositiveInteger
         xsd:normalizedString
         xsd:positiveInteger
         xsd:short
         xsd:string
         xsd:time
         xsd:token
         xsd:unsignedByte
         xsd:unsignedInt
         xsd:unsignedLong
         xsd:unsignedShort
    
    Bindings:
         Soap11Binding: {http://tempuri.org/}CalculatorSoap
         Soap12Binding: {http://tempuri.org/}CalculatorSoap12
    
    Service: Calculator
         Port: CalculatorSoap (Soap11Binding: {http://tempuri.org/}CalculatorSoap)
             Operations:
                Add(intA: xsd:int, intB: xsd:int) -> AddResult: xsd:int
                Divide(intA: xsd:int, intB: xsd:int) -> DivideResult: xsd:int
                Multiply(intA: xsd:int, intB: xsd:int) -> MultiplyResult: xsd:int
                Subtract(intA: xsd:int, intB: xsd:int) -> SubtractResult: xsd:int
    
         Port: CalculatorSoap12 (Soap12Binding: {http://tempuri.org/}CalculatorSoap12)
             Operations:
                Add(intA: xsd:int, intB: xsd:int) -> AddResult: xsd:int
                Divide(intA: xsd:int, intB: xsd:int) -> DivideResult: xsd:int
                Multiply(intA: xsd:int, intB: xsd:int) -> MultiplyResult: xsd:int
                Subtract(intA: xsd:int, intB: xsd:int) -> SubtractResult: xsd:int
    

    If you look at the result of this when running the command on your WSDL, you will see a "Bindings" section. That's from where you get the value, which most likely will be the one for the Soap11Binding (most services will provide just one binding, this one provides two, one for each version of the SOAP protocol).

    For the http://my-endpoint.com/acceptance/ in the documentation, you should replace your new SOAP address (i.e. the new place where you want to send the service request now). Judging from your example, this should be http://virtual-soap/something, where something is the original path from the original SOAP address that's inside the WSDL (again, since I don't have access to you WSDL, you should look to see which part of the address you need to replace, and which stays the same).

    As for what operation you want to call, that doesn't changes. You call it the same way. The same methods are there because you don't change the service contract, you just change where you want to send the request to.