system.net.httpGet vs requests.get

Hi,

I have been using Ignitions built in function system.net.httpGet(url,headerValues,useCaches)
Which has been working, but is not providing any useful info when something goes wrong.
So in order to examine the http Status Code and body of the response, I wanted to try and use the Python Library ‘requests’.

The API I’m using requires an api-auth-signature in the header, which is an encrypted signature. This following code works:

urlHeader = {"Accept": "application/json", "api-auth-id": apiID, "api-auth-signature": signature, "Content-Type": "application/json"}
response = system.net.httpGet(url, headerValues=urlHeader, useCaches=False)
data = system.util.jsonDecode(response)
print data

But when i try do the equivalent using the requests library as below, i get a 403 error: “Authentication denied - signature mismatch.”

urlHeader = {"Accept": "application/json", "api-auth-id": apiID, "api-auth-signature": signature, "Content-Type": "application/json"}
response = core.get(url,headers=urlHeader)
print response.status_code
print response.content

Does anyone know what might be different between the two functions?
Does python requests library not have support for encrypted keys?

I’m using the following to generate the ‘signature’ variable.

message = request.encode('utf-8')
secret = apiKEY.encode('utf-8')
signature = base64.b64encode(hmac.new(secret,message, digestmod=hashlib.sha256).digest())

Have you tried a Wireshark capture to see what the two different outgoing packets actually look like/have in their HTTP headers?

Also, I’m not too familiar with the requests module, but urllib2 offers the same benefits and is a little bit lower level, potentially allowing more abstraction.

The urllib2 is even worse for handling the HTTP status codes. I tried that way back when i started, and discovered it throws a python exception on some 20X status codes when it shouldn’t.

I’ve got Wireshark running but pinning down the packets is proving difficult. Do you have suggestions on what to filter on?
I tried filtering http.request.method==GET. but didn’t get what i needed from that

If not Wireshark then send your request to http://requestb.in or such.

It is a pain, but you can catch the different status codes from urllib2 -

try:
	request = urllib2.Request(url)
	request.add_header('authorization', 'Bearer %s' % token)
	outbound = urllib2.urlopen(request)
except urllib2.HTTPError, e:
	if e.code == 403:
		pass

For what its worth (and for other potential future visitors to this thread), we had some requirements for implementing HTTP requests in a recent project. I looked at requests and unfortunately it only supports Python 2.6 or higher. I tried using urllib2 next and though I can’t remember the specific troubles we ended up having, it didn’t work out (perhaps it had to do with using digest authentication, which was the reason we couldn’t use the built-in Ignition offerings to begin with).

We ended up implementing a custom module that utilized the Apache HttpClient module of HttpComponents. It worked out quite nicely, FWIW.

What kind of error information are you looking for?

Kevin - what Python version does Ignition use then? Requests works to some degree for me. I too am reluctant to use urllub2 due to its not very good handling of status codes.

Cody - I’m just wanting to capture any http error returned from the server due to a bad request. I want to know why the request was rejected so it can be captured in error logs and dealt to. Unfortunately Ignition’s system.net.httpGet doesn’t return the HTTP status code. It can return the error body. but unfortunately that also returns something on a successful request, so it’s not too helpful.

Is there a better tool for analyzing http requests that are sent out? I think i managed to find some packets in Wireshark, but I couldn’t really identify anything useful.

Looks like the problem was how the URL parameters were being passed.
http://docs.python-requests.org/en/master/user/quickstart/#passing-parameters-in-urls
Instead of manually adding the parameters to the URL, I added them via the requests.get function, and that seems to have done the trick.

So instead of:

api = "https://api.unleashedsoftware.com/"
endpoint = "Product"
request = "ProductCode=5020&includeAttributes=TRUE"
url = api + endpoint + requestUrl
resp = requests.get(url,headers=urlHeader)

I changed it to:

api = "https://api.unleashedsoftware.com/"
endpoint = "Product"
paramsDict = {'ProductCode': '5020', "includeAttributes": "TRUE"}
url = api + endpoint
resp = requests.get(url,headers=urlHeader, params=paramsDict)

However i still need the string

request = "ProductCode=5020&includeAttributes=TRUE"

to be hashed into the signature, and these parameters have to match the parameters in the paramsDict.