Strange behavior from HTTP authentication with suds SOAP library
Asked Answered
D

1

6

I have a working python program that is fetching a large volume of data via SOAP using suds. The web service is implemented with a paging function such that I can grab nnn rows with each fetch call and grab the next nnn with subsequent calls. If I authenticate to the HTTP server with code like the following

client = suds.client.Client(url=url, location=location, username=username, password=password, timeout=timeout)

everything works great. If, however, I use the following

t = suds.transport.https.HttpAuthenticated(username=username, password=password)
t.handler = urllib2.HTTPBasicAuthHandler(t.pm)
t.urlopener = urllib2.build_opener(t.handler)
client = suds.client.Client(url=url, location=location, timeout=timeout, transport=t) 

it works for exactly 6 iterations. That is if I specify a fetch limit of 10 rows per fetch, I get back 60 rows. On the seventh fetch, I receive

  File "build/bdist.linux-i686/egg/suds/client.py", line 542, in __call__
  File "build/bdist.linux-i686/egg/suds/client.py", line 602, in invoke
  File "build/bdist.linux-i686/egg/suds/client.py", line 649, in send
  File "build/bdist.linux-i686/egg/suds/client.py", line 698, in failed
AttributeError: 'NoneType' object has no attribute 'read'

Does anyone have any suggestions as to what might be causing this. It is definitely this change that is causing the problem. I can swap authentication styles back and forth and it is completely reproducible.

I am running python 2.6.6 with suds 0.4.

Thanks

Disqualification answered 5/3, 2011 at 1:33 Comment(1)
Any progress with this? If I pass wrong authentication data to the constructor, I get the same error instead of an exception. If I use HttpAuthenticated to connect and pass that to the suds client constructor, I always get 403 Bad request (invalid authentication) even if the authentication data is valid.Deportation
S
10

The problem seems to be that an urllib2.HTTPError is being raised from a lower level, and its fp attribute is None:

Line 81 in suds.transport.http:

except u2.HTTPError, e:
    if e.code in (202,204):
        result = None
    else:
        raise TransportError(e.msg, e.code, e.fp)

That exception eventually gets passed to line 698 in suds.client where that error.fp.read() line blows up:

def failed(self, binding, error):
    status, reason = (error.httpcode, tostr(error))
    reply = error.fp.read()

I'd suggest monkey-patching the suds.SoapClient class to get the HTTP error code and message. Add these lines before you construct your suds.Client, then run it to see what HTTP error the 7th fetch raises:

class TestClient(suds.client.SoapClient):
    def failed(self, binding, error):
        print error.httpcode, error.args

suds.client.SoapClient = TestClient
Stephanus answered 7/3, 2011 at 5:57 Comment(3)
On the 7'th execution of the fetch call, I received "401 ('basic auth failed',), None". If I pass the username and password to the client constructor, I receive no such error.Disqualification
Maybe do a lower-level urllib2 debug to make sure your transport/handelrs are being used on that 7th pass. Suds might be switching back to using its default transport for some reason, which fails when no user/pass are provided.Stephanus
I used your patch but instead of the print.., I used raise error and everything worked as expected. Thanks!Deportation

© 2022 - 2024 — McMap. All rights reserved.