I have the following (with some strings modified) which works when using the requests
library.
import requests
from pprint import pprint
import json
PEM = '/full/path/to/my.pem'
client_id='cliendID'
client_secret='clientSecret'
USER='myuser'
PSWD='mypwd'
url = 'https://theurl.com/request/'
data = {
"grant_type": "password",
"username": USER,
"password": PSWD
}
auth = (client_id, client_secret)
response = requests.post( url, auth=auth, data=data, verify=PEM )
answer = response.json()['answer']
print( answer )
The answer printed is what I expect.
The following usage of curl
also works:
curl -iv --user cliendID:clientSecret --key /full/path/to/server.key --cert /full/path/to/my.pem -F grant_type=password -F username=myuser -F password=mypwd https://theurl.com/request/
However, when I try to do the same using Tornado and AsyncHTTPClient, I get a "Bad Request" response. Some sample Tornado code is:
from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop
import json
async def get_content():
PEM = '/full/path/to/my.pem'
client_id='cliendID'
client_secret='clientSecret'
url = 'https://theurl.com/request/'
USER='myuser'
PSWD='mypwd'
data = {
"grant_type": "password",
"username": USER,
"password": PSWD
}
bodyData = json.dumps( data )
http_client = AsyncHTTPClient()
response = await http_client.fetch( url,
method = 'POST',
body = bodyData,
auth_username = client_id,
auth_password = client_secret,
ca_certs = PEM )
print( response.body.decode() )
async def main():
await get_content()
if __name__ == "__main__":
io_loop = IOLoop.current()
io_loop.run_sync(main)
If I had to guess, I believe the issue is with how I am sending the bodyData.
What do I need to change in the Tornado code so this will work...?
By default, the requests
library and Tornado's AsyncHTTPClient
, both, send the body data as form encoded data (i.e. with Content-Type: application/x-www-form-urlencoded
).
The requests
library automatically encodes the data dict with the correct content type.
But with Tornado's client, you're requried to manually encode the data as follows:
from urllib.parse import urlencode
bodyData = urlencode(data)
...
Cause of the error:
You're getting the Bad Request error because you're sending the data as JSON but the Content-Type header (sent automatically by Tornado) still says www-form-urlencoded
. That means the server can't decode your supplied data because it doesn't know that it's JSON.