I need to issue HTTPS GETs from within my Panda3d application to a site that uses basic authentication. This is my first time using HTTPSClient and HTTPSChannel. I haven’t found how to get them to successfully handle basic authentication. When I run the following code, I’m expecting the returned data to be the requested document. Instead, I receive a login page.
The login page response indicates that the server did not recognize an acceptable Authorization header such as “Authorization: Basic userid:password”. It has returned a login page for me to interactively login.
The site returns the expected document when I access it using the python requests HTTPS library and the Postman application, but not when using HTTPClient+HTTPChannel. What am I missing in setting up basic authentication?
server = 'ncs01.case.edu'
url = 'https://' + server + '/webacs/api/v4/data/AccessPointDetails.json?.full=true&.maxResults=1000&.nocount=true'
client = HTTPClient()
client.add_direct_host(server)
client.set_verify_ssl(HTTPClient.VS_no_verify)
user, password = mylib.credentials(server, None) # get my default credentials for server
user_password = HTTPClient.base64_encode(f"{user}:{password}")
client.set_username(server, '', user_password)
chan: HTTPChannel = client.make_channel(False)
chan.beginConnectTo(url) # non-blocking direct connection
while chan.run():
time.sleep(0.1)
print(f"is_connection_ready={chan.is_connection_ready()}")
chan.beginGetDocument(url) # sends non-blocking GET?
while chan.run(): # turns False with operation is complete
time.sleep(0.1)
if not chan.is_valid():
raise ValueError(f"invalid url")
status = chan.get_status_code()
if status == 200: # success
stream: 'ISocketStream' = chan.open_read_body()
data = bytes()
reader = StreamReader(stream, True)
while True:
s = reader.extract_bytes(100000)
print(f"extracted {len(s)} bytes")
if len(s) == 0: # stream EOF?
if stream.is_closed(): # document end?
print(f"stream is_closed") # Yes
break # end of data
else: # No. More to come
time.sleep(0.1)
else: # has data
data += s # aggregate segment into data
# Received whole document
print(f"Received {len(data)} bytes")
ss = str(data, 'UTF-8')
print(ss)
else:
print(f"get_status_code={status}")
Just specify “user:password”, Panda will do the base64 step, I think it’s currently being doubly encoded.
I also suggest leaving the server argument blank to be sure that Panda will pick the chosen username for this request.
Note that 100 ms is a long time to sleep while handling the request. If you want to do it synchronously, you can just use getDocument instead of beginGetDocument.
Could it be that the server is not returning a 401 error with the WWW-Authenticate header? I think HTTPChannel expects that to be sent in order to retry with authentication.
You can always set the header yourself using channel.send_extra_header.
In another trial, instead of putting the send_extra_header just prior to the beginConnect(url), I tried putting the send_extra_header just prior to the beginGetDocument. This causes chan.is_valid() to return false.
server = 'ncs01.case.edu'
url = 'https://' + server + '/webacs/api/v4/data/AccessPointDetails.json?.full=true&.maxResults=1000&.nocount=true'
client = HTTPClient()
client.add_direct_host(server)
client.set_verify_ssl(HTTPClient.VS_no_verify)
user, password = mylib.credentials(server, None) # get my default credentials for server
client.set_username('', '', f"{user}:{password}")
chan: HTTPChannel = client.make_channel(False)
chan.beginConnectTo(url) # non-blocking direct connection
while chan.run():
time.sleep(0.1)
print(f"is_connection_ready={chan.is_connection_ready()}")
chan.send_extra_header('Authorization', f"Basic {user}:{password}")
chan.beginGetDocument(url) # sends non-blocking GET?
while chan.run(): # turns False with operation is complete
time.sleep(0.1)
if not chan.is_valid():
raise ValueError(f"invalid url")
...