Skip to content

Commit f97cb7c

Browse files
committed
Merge remote-tracking branch 'oauth2cli/dev' into ctrlc
2 parents 059fcc6 + 7de3224 commit f97cb7c

File tree

2 files changed

+36
-7
lines changed

2 files changed

+36
-7
lines changed

msal/oauth2cli/authcode.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import logging
99
import socket
1010
from string import Template
11+
import threading
12+
import time
1113

1214
try: # Python 3
1315
from http.server import HTTPServer, BaseHTTPRequestHandler
@@ -149,11 +151,7 @@ def get_port(self):
149151
# https://docs.python.org/2.7/library/socketserver.html#SocketServer.BaseServer.server_address
150152
return self._server.server_address[1]
151153

152-
def get_auth_response(self, auth_uri=None, timeout=None, state=None,
153-
welcome_template=None, success_template=None, error_template=None,
154-
auth_uri_callback=None,
155-
browser_name=None,
156-
):
154+
def get_auth_response(self, timeout=None, **kwargs):
157155
"""Wait and return the auth response. Raise RuntimeError when timeout.
158156
159157
:param str auth_uri:
@@ -192,6 +190,37 @@ def get_auth_response(self, auth_uri=None, timeout=None, state=None,
192190
and https://openid.net/specs/openid-connect-core-1_0.html#AuthResponse
193191
Returns None when the state was mismatched, or when timeout occurred.
194192
"""
193+
# Historically, the _get_auth_response() uses HTTPServer.handle_request(),
194+
# because its handle-and-retry logic is conceptually as easy as a while loop.
195+
# Also, handle_request() honors server.timeout setting, and CTRL+C simply works.
196+
# All those are true when running on Linux.
197+
#
198+
# However, the behaviors on Windows turns out to be different.
199+
# A socket server waiting for request would freeze the current thread.
200+
# Neither timeout nor CTRL+C would work. End user would have to do CTRL+BREAK.
201+
# https://stackoverflow.com/questions/1364173/stopping-python-using-ctrlc
202+
#
203+
# The solution would need to somehow put the http server into its own thread.
204+
# This could be done by the pattern of ``http.server.test()`` which internally
205+
# use ``ThreadingHTTPServer.serve_forever()`` (only available in Python 3.7).
206+
# Or create our own thread to wrap the HTTPServer.handle_request() inside.
207+
result = {} # A mutable object to be filled with thread's return value
208+
t = threading.Thread(
209+
target=self._get_auth_response, args=(result,), kwargs=kwargs)
210+
t.daemon = True # So that it won't prevent the main thread from exiting
211+
t.start()
212+
begin = time.time()
213+
while (time.time() - begin < timeout) if timeout else True:
214+
time.sleep(1) # Short detection interval to make happy path responsive
215+
if not t.is_alive(): # Then the thread has finished its job and exited
216+
break
217+
return result or None
218+
219+
def _get_auth_response(self, result, auth_uri=None, timeout=None, state=None,
220+
welcome_template=None, success_template=None, error_template=None,
221+
auth_uri_callback=None,
222+
browser_name=None,
223+
):
195224
welcome_uri = "http://localhost:{p}".format(p=self.get_port())
196225
abort_uri = "{loc}?error=abort".format(loc=welcome_uri)
197226
logger.debug("Abort by visit %s", abort_uri)
@@ -238,7 +267,7 @@ def get_auth_response(self, auth_uri=None, timeout=None, state=None,
238267
logger.debug("State mismatch. Ignoring this noise.")
239268
else:
240269
break
241-
return self._server.auth_response
270+
result.update(self._server.auth_response) # Return via writable result param
242271

243272
def close(self):
244273
"""Either call this eventually; or use the entire class as context manager"""

msal/oauth2cli/oauth2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def _obtain_token( # The verb "obtain" is influenced by OAUTH2 RFC 6749
199199
_data["client_assertion"] = encoder(
200200
self.client_assertion() # Do lazy on-the-fly computation
201201
if callable(self.client_assertion) else self.client_assertion
202-
) # The type is bytes, which is preferrable. See also:
202+
) # The type is bytes, which is preferable. See also:
203203
# https://github.com/psf/requests/issues/4503#issuecomment-455001070
204204

205205
_data.update(self.default_body) # It may contain authen parameters

0 commit comments

Comments
 (0)