0

I am using django behind nginx reverse proxy and django sees the server url different than what it actually is hosted on like: Django: http://webserver.com

Nginx: https://webserver.com

When I try to add the WSDL to SoapUI it automatically defaults to the first http://webserver.com server and then all the requests fail. I have tried the code below, but it did not work:

...
app = Application(
    [EXTWS],
    tns='soap.views',
    in_protocol=Soap11(validator='soft'),
    out_protocol=Soap11(),
)

app.transport = "no_transport_at_all"

...


wsdl = Wsdl11(app.interface)

if os.environ.get("DEBUG"):
    wsdl.build_interface_document('http://localhost:8000/wsdl/')
else:
    url = f'https://{settings.DOMAIN}/wsdl/'
    wsdl.build_interface_document(url)

Inspirations: here and here

EDIT:

Looks like the code above achieves some things but the resulting WSDL document when accessed in browser is still the same, maybe it is generated on request; the documentation said "... Spyne will get the URL from the first request, build the wsdl on-the-fly and cache it as a string in memory for later requests." but here it is generated manually, so it should not generate a new one maybe? Or it is generating it by request because it is django, not wsgi.

enter image description here

EDIT:

Looks like building the tree by hand does not make any difference as when you send the first requests, a new instance of Wsdl11 class is generated.

Guven Degirmenci
  • 684
  • 7
  • 16

1 Answers1

0

Temporarily, I achieved changing the url to what I want by basically monkey patching the two classes as follows:


from functools import update_wrapper
from spyne.server.http import HttpBase, HttpMethodContext, HttpTransportContext
from spyne.application import get_fault_string_from_exception, Application
from django.http import HttpResponse, HttpResponseNotAllowed, Http404


class MonkeyDjangoServer(DjangoServer):
    def handle_wsdl(self, request, *args, **kwargs):
        """Return services WSDL."""
        ctx = HttpMethodContext(self, request,
                                'text/xml; charset=utf-8')

        if self.doc.wsdl11 is None:
            raise Http404('WSDL is not available')

        if self._wsdl is None:
            # Interface document building is not thread safe so we don't use
            # server interface document shared between threads. Instead we
            # create and build interface documents in current thread. This
            # section can be safely repeated in another concurrent thread.
            self.doc.wsdl11.service_elt_dict = {}

            #                       here you can put whatever you want
            self.doc.wsdl11.build_interface_document("http://MONKEY/")
            wsdl = self.doc.wsdl11.get_interface_document()

            if self._cache_wsdl:
                self._wsdl = wsdl
        else:
            wsdl = self._wsdl

        ctx.transport.wsdl = wsdl

        response = HttpResponse(ctx.transport.wsdl)
        return self.response(response, ctx, ())


class MonkeyDjangoView(DjangoView):
    @classmethod
    def as_view(cls, **initkwargs):
        """Register application, server and create new view.

        :returns: callable view function
        """

        # sanitize keyword arguments
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__,
                                                              key))

        def get(key):
            value = initkwargs.get(key)
            return value if value is not None else getattr(cls, key)

        def pop(key):
            value = initkwargs.pop(key, None)
            return value if value is not None else getattr(cls, key)

        application = get('application') or Application(
            services=get('services'),
            tns=get('tns'),
            name=get('name'),
            in_protocol=get('in_protocol'),
            out_protocol=get('out_protocol'),
        )
        server = pop('server') or MonkeyDjangoServer(application,
                                               chunked=get('chunked'),
                                               cache_wsdl=get('cache_wsdl'))

        def view(request, *args, **kwargs):
            self = cls(server=server, **initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view


urlpatterns = [
    url(r'^EXTWS/$', MonkeyDjangoView.as_view(application=app)),
    # url(r'^schema/$', get_schema),
]

This is some unacceptable kind of solution so I will be waiting for a logical implementation of this behavior. Until then, I will be using this.

Guven Degirmenci
  • 684
  • 7
  • 16