class HttpClient(object): def __init__(self, client, adapter_kwargs=None, keep_alive=True): self.client = client self.adapter_kwargs = adapter_kwargs or {} self.keep_alive = keep_alive # Build client self.configuration = ContextStack() self.session = None self._proxies = {} self._ssl_version = None self._oauth_refreshing = KeyLock() self._oauth_validate_lock = RLock() # Build requests session self.rebuild() @property def proxies(self): if self.session and self.session.proxies: return self.session.proxies return self._proxies @proxies.setter def proxies(self, proxies): if self.session: self.session.proxies = proxies self._proxies = proxies @property def ssl_version(self): return self._ssl_version @ssl_version.setter def ssl_version(self, version): self._ssl_version = version # Rebuild session (to apply ssl version change) self.rebuild() def configure(self, path=None): self.configuration.push(base_path=path) return self def request(self, method, path=None, params=None, data=None, query=None, authenticated=False, validate_token=True, **kwargs): # Retrieve configuration ctx = self.configuration.pop() # Build request request = TraktRequest( self.client, method=method, path=self._build_path(ctx, path), params=params, data=data, query=query, authenticated=authenticated, **kwargs ) # Validate authentication details (OAuth) if authenticated and validate_token and not self.validate(): return None # Prepare request prepared = request.prepare() if not self.keep_alive: prepared.headers['Connection'] = 'close' # Send request return self.send(prepared) def send(self, request): # Retrieve http configuration retry = self.client.configuration.get('http.retry', DEFAULT_HTTP_RETRY) max_retries = self.client.configuration.get('http.max_retries', DEFAULT_HTTP_MAX_RETRIES) retry_sleep = self.client.configuration.get('http.retry_sleep', DEFAULT_HTTP_RETRY_SLEEP) timeout = self.client.configuration.get('http.timeout', DEFAULT_HTTP_TIMEOUT) # Send request response = None for i in xrange(max_retries + 1): if i > 0: log.warn('Retry # %s', i) # Send request try: response = self.session.send(request, timeout=timeout) except socket.gaierror as e: code, __ = e if code != 8: raise e log.warn('Encountered socket.gaierror (code: 8)') response = self.rebuild().send(request, timeout=timeout) # Retry requests on errors >= 500 (when enabled) if not retry or response.status_code < 500: break log.warn('Continue retry since status is %s, waiting %s seconds', response.status_code, retry_sleep) time.sleep(retry_sleep) return response def delete(self, path=None, params=None, data=None, **kwargs): return self.request('DELETE', path, params, data, **kwargs) def get(self, path=None, params=None, data=None, **kwargs): return self.request('GET', path, params, data, **kwargs) def post(self, path=None, params=None, data=None, **kwargs): return self.request('POST', path, params, data, **kwargs) def put(self, path=None, params=None, data=None, **kwargs): return self.request('PUT', path, params, data, **kwargs) def rebuild(self): if self.session: log.info('Rebuilding session and connection pools...') # Build the connection pool self.session = requests.Session() self.session.proxies = self.proxies # Mount adapters self.session.mount('http://', HTTPAdapter(**self.adapter_kwargs)) self.session.mount('https://', HTTPSAdapter(ssl_version=self._ssl_version, **self.adapter_kwargs)) return self.session def validate(self): config = self.client.configuration # xAuth if config['auth.login'] and config['auth.token']: return True # OAuth if config['oauth.token']: # Validate OAuth token, refresh if needed return self._validate_oauth() return False def _build_path(self, ctx, path): if not ctx: # No context available return path if ctx.base_path and path: # Prepend `base_path` to relative `path`s if not path.startswith('/'): path = ctx.base_path + '/' + path elif ctx.base_path: # Set path to `base_path path = ctx.base_path return path @synchronized(lambda self: self._oauth_validate_lock) def _validate_oauth(self): config = self.client.configuration # Ensure token expiry is available if config['oauth.created_at'] is None or config['oauth.expires_in'] is None: log.debug('OAuth - Missing "created_at" or "expires_in" parameters, ' 'unable to determine if the current token is still valid') return True # Calculate expiry current = calendar.timegm(datetime.datetime.utcnow().utctimetuple()) expires_at = config['oauth.created_at'] + config['oauth.expires_in'] - (48 * 60 * 60) if current < expires_at: return True if not config['oauth.refresh']: log.warn('OAuth - Unable to refresh expired token (token refreshing hasn\'t been enabled)') return False if not config['oauth.refresh_token']: log.warn('OAuth - Unable to refresh expired token ("refresh_token" parameter hasn\'t been defined)') return False # Retrieve username username = config['oauth.username'] if not username: log.info('OAuth - Current username is not available ("username" parameter hasn\'t been defined)') # Acquire refreshing lock if not self._oauth_refreshing[username].acquire(False): log.warn('OAuth - Token is already being refreshed for %r', username) return False log.info('OAuth - Token has expired, refreshing token...') # Refresh token try: if not self._refresh_oauth(): return False log.info('OAuth - Token has been refreshed') return True finally: # Release refreshing lock self._oauth_refreshing[username].release() def _refresh_oauth(self): config = self.client.configuration # Refresh token response = self.client['oauth'].token_refresh( config['oauth.refresh_token'], 'urn:ietf:wg:oauth:2.0:oob', parse=False ) if response is None: log.warn('OAuth - Unable to refresh expired token (no response returned)') return False if response.status_code < 200 or response.status_code >= 300: # Clear current configuration config.current.oauth.clear() # Handle refresh rejection if response.status_code == 401: log.warn('OAuth - Unable to refresh expired token (rejected)') # Fire rejected event self.client.emit('oauth.refresh.rejected', config['oauth.username']) return False # Unknown error returned log.warn('OAuth - Unable to refresh expired token (code: %r)', response.status_code) return False # Retrieve authorization parameters from response authorization = response.json() # Update current configuration config.current.oauth.from_response( authorization, refresh=config['oauth.refresh'], username=config['oauth.username'] ) # Fire refresh event self.client.emit('oauth.refresh', config['oauth.username'], authorization) # Fire legacy refresh event self.client.emit('oauth.token_refreshed', authorization) return True
class HttpClient(object): def __init__(self, client, adapter_kwargs=None): self.client = client self.adapter_kwargs = adapter_kwargs or {} # Build client self.configuration = ContextStack() self.session = None self.rebuild() def configure(self, path=None): self.configuration.push(base_path=path) return self def request(self, method, path=None, params=None, data=None, query=None, **kwargs): # retrieve configuration ctx = self.configuration.pop() retry = self.client.configuration.get('http.retry', DEFAULT_HTTP_RETRY) max_retries = self.client.configuration.get('http.max_retries', DEFAULT_HTTP_MAX_RETRIES) retry_sleep = self.client.configuration.get('http.retry_sleep', DEFAULT_HTTP_RETRY_SLEEP) timeout = self.client.configuration.get('http.timeout', DEFAULT_HTTP_TIMEOUT) # build request if ctx.base_path and path: path = ctx.base_path + '/' + path elif ctx.base_path: path = ctx.base_path request = TraktRequest( self.client, method=method, path=path, params=params, data=data, query=query, **kwargs ) prepared = request.prepare() # retrying requests on errors >= 500 response = None for i in range(max_retries + 1): if i > 0 : log.warn('Retry # %s', i) try: response = self.session.send(prepared, timeout=timeout) except socket.gaierror as e: code, _ = e if code != 8: raise e log.warn('Encountered socket.gaierror (code: 8)') response = self.rebuild().send(prepared, timeout=timeout) if not retry or response.status_code < 500: break log.warn('Continue retry since status is %s, waiting %s seconds', response.status_code, retry_sleep) time.sleep(retry_sleep) return response def get(self, path=None, params=None, data=None, **kwargs): return self.request('GET', path, params, data, **kwargs) def post(self, path=None, params=None, data=None, **kwargs): return self.request('POST', path, params, data, **kwargs) def delete(self, path=None, params=None, data=None, **kwargs): return self.request('DELETE', path, params, data, **kwargs) def rebuild(self): if self.session: log.info('Rebuilding session and connection pools...') # Build the connection pool self.session = requests.Session() self.session.mount('http://', HTTPAdapter(**self.adapter_kwargs)) self.session.mount('https://', HTTPAdapter(**self.adapter_kwargs)) return self.session
class HttpClient(object): def __init__(self, client, adapter_kwargs=None): self.client = client self.adapter_kwargs = adapter_kwargs or {} # Build client self.configuration = ContextStack() self.session = None self._proxies = {} self._validate_oauth_lock = Lock() self.rebuild() @property def proxies(self): if self.session and self.session.proxies: return self.session.proxies return self._proxies @proxies.setter def proxies(self, proxies): if self.session: self.session.proxies = proxies self._proxies = proxies def configure(self, path=None): self.configuration.push(base_path=path) return self def request(self, method, path=None, params=None, data=None, query=None, authenticated=False, **kwargs): # retrieve configuration ctx = self.configuration.pop() retry = self.client.configuration.get('http.retry', DEFAULT_HTTP_RETRY) max_retries = self.client.configuration.get('http.max_retries', DEFAULT_HTTP_MAX_RETRIES) retry_sleep = self.client.configuration.get('http.retry_sleep', DEFAULT_HTTP_RETRY_SLEEP) timeout = self.client.configuration.get('http.timeout', DEFAULT_HTTP_TIMEOUT) # build request if ctx.base_path and path: # Prepend `base_path` to relative `path`s if not path.startswith('/'): path = ctx.base_path + '/' + path elif ctx.base_path: path = ctx.base_path request = TraktRequest( self.client, method=method, path=path, params=params, data=data, query=query, authenticated=authenticated, **kwargs ) # Validate authentication details (OAuth) if authenticated and not self.validate(): return None # Prepare request prepared = request.prepare() response = None for i in range(max_retries + 1): if i > 0: log.warn('Retry # %s', i) # Send request try: response = self.session.send(prepared, timeout=timeout) except socket.gaierror as e: code, _ = e if code != 8: raise e log.warn('Encountered socket.gaierror (code: 8)') response = self.rebuild().send(prepared, timeout=timeout) # Retry requests on errors >= 500 (when enabled) if not retry or response.status_code < 500: break log.warn('Continue retry since status is %s, waiting %s seconds', response.status_code, retry_sleep) time.sleep(retry_sleep) return response def delete(self, path=None, params=None, data=None, **kwargs): return self.request('DELETE', path, params, data, **kwargs) def get(self, path=None, params=None, data=None, **kwargs): return self.request('GET', path, params, data, **kwargs) def post(self, path=None, params=None, data=None, **kwargs): return self.request('POST', path, params, data, **kwargs) def put(self, path=None, params=None, data=None, **kwargs): return self.request('PUT', path, params, data, **kwargs) def rebuild(self): if self.session: log.info('Rebuilding session and connection pools...') # Build the connection pool self.session = requests.Session() self.session.proxies = self.proxies # Mount adapters self.session.mount('http://', HTTPAdapter(**self.adapter_kwargs)) if ssl is not None: self.session.mount('https://', HTTPSAdapter(ssl_version=ssl.PROTOCOL_TLSv1, **self.adapter_kwargs)) else: log.warn('"ssl" module is not available, unable to change "ssl_version"') self.session.mount('https://', HTTPSAdapter(**self.adapter_kwargs)) return self.session def validate(self): config = self.client.configuration # xAuth if config['auth.login'] and config['auth.token']: return True # OAuth if config['oauth.token']: # Validate OAuth token, refresh if needed return self._validate_oauth() return False @synchronized(lambda self: self._validate_oauth_lock) def _validate_oauth(self): config = self.client.configuration if config['oauth.created_at'] is None or config['oauth.expires_in'] is None: log.debug('OAuth - Missing "created_at" or "expires_in" parameter, ' 'unable to determine if token is still valid') return True current = calendar.timegm(datetime.datetime.utcnow().utctimetuple()) expires_at = config['oauth.created_at'] + config['oauth.expires_in'] - (48 * 60 * 60) if current < expires_at: return True if not config['oauth.refresh']: log.warn('OAuth - Unable to refresh expired token (token refreshing hasn\'t been enabled)') return False if not config['oauth.refresh_token']: log.warn('OAuth - Unable to refresh expired token ("refresh_token" is parameter is missing)') return False # Refresh token response = self.client['oauth'].token_refresh(config['oauth.refresh_token'], 'urn:ietf:wg:oauth:2.0:oob') if not response: log.warn('OAuth - Unable to refresh expired token (error occurred while trying to refresh the token)') return False # Update current configuration config.current.oauth.from_response(response) # Fire refresh event self.client.emit('oauth.token_refreshed', response) return True
class HttpClient(object): def __init__(self, client, adapter_kwargs=None): self.client = client self.adapter_kwargs = adapter_kwargs or {} # Build client self.configuration = ContextStack() self.session = None self._proxies = {} self._validate_oauth_lock = Lock() self.rebuild() @property def proxies(self): if self.session and self.session.proxies: return self.session.proxies return self._proxies @proxies.setter def proxies(self, proxies): if self.session: self.session.proxies = proxies self._proxies = proxies def configure(self, path=None): self.configuration.push(base_path=path) return self def request(self, method, path=None, params=None, data=None, query=None, authenticated=False, **kwargs): # retrieve configuration ctx = self.configuration.pop() retry = self.client.configuration.get('http.retry', DEFAULT_HTTP_RETRY) max_retries = self.client.configuration.get('http.max_retries', DEFAULT_HTTP_MAX_RETRIES) retry_sleep = self.client.configuration.get('http.retry_sleep', DEFAULT_HTTP_RETRY_SLEEP) timeout = self.client.configuration.get('http.timeout', DEFAULT_HTTP_TIMEOUT) # build request if ctx.base_path and path: path = ctx.base_path + '/' + path elif ctx.base_path: path = ctx.base_path request = TraktRequest(self.client, method=method, path=path, params=params, data=data, query=query, authenticated=authenticated, **kwargs) # Validate authentication details (OAuth) if authenticated and not self.validate(): return None # Prepare request prepared = request.prepare() response = None for i in range(max_retries + 1): if i > 0: log.warn('Retry # %s', i) # Send request try: response = self.session.send(prepared, timeout=timeout) except socket.gaierror as e: code, _ = e if code != 8: raise e log.warn('Encountered socket.gaierror (code: 8)') response = self.rebuild().send(prepared, timeout=timeout) # Retry requests on errors >= 500 (when enabled) if not retry or response.status_code < 500: break log.warn('Continue retry since status is %s, waiting %s seconds', response.status_code, retry_sleep) time.sleep(retry_sleep) return response def get(self, path=None, params=None, data=None, **kwargs): return self.request('GET', path, params, data, **kwargs) def post(self, path=None, params=None, data=None, **kwargs): return self.request('POST', path, params, data, **kwargs) def delete(self, path=None, params=None, data=None, **kwargs): return self.request('DELETE', path, params, data, **kwargs) def rebuild(self): if self.session: log.info('Rebuilding session and connection pools...') # Build the connection pool self.session = requests.Session() self.session.proxies = self.proxies # Mount adapters self.session.mount('http://', HTTPAdapter(**self.adapter_kwargs)) self.session.mount('https://', HTTPAdapter(**self.adapter_kwargs)) return self.session def validate(self): config = self.client.configuration # xAuth if config['auth.login'] and config['auth.token']: return True # OAuth if config['oauth.token']: # Validate OAuth token, refresh if needed return self._validate_oauth() return False @synchronized(lambda self: self._validate_oauth_lock) def _validate_oauth(self): config = self.client.configuration if config['oauth.created_at'] is None or config[ 'oauth.expires_in'] is None: log.debug( 'OAuth - Missing "created_at" or "expires_in" parameter, unable to determine if token is still valid' ) return True current = calendar.timegm(datetime.datetime.utcnow().utctimetuple()) expires_at = config['oauth.created_at'] + config[ 'oauth.expires_in'] - (48 * 60 * 60) if current < expires_at: return True if not config['oauth.refresh']: log.warn( 'OAuth - Unable to refresh expired token (token refreshing hasn\'t been enabled)' ) return False if not config['oauth.refresh_token']: log.warn( 'OAuth - Unable to refresh expired token ("refresh_token" is parameter is missing)' ) return False # Refresh token response = self.client['oauth'].token_refresh( config['oauth.refresh_token'], 'urn:ietf:wg:oauth:2.0:oob') if not response: log.warn( 'OAuth - Unable to refresh expired token (error occurred while trying to refresh the token)' ) return False # Update current configuration config.current.oauth.from_response(response) # Fire refresh event self.client.emit('oauth.token_refreshed', response) return True
class HttpClient(object): def __init__(self, client, adapter_kwargs=None): self.client = client self.adapter_kwargs = adapter_kwargs or {} # Build client self.configuration = ContextStack() self.session = None self._build_session() def configure(self, path=None): self.configuration.push(base_path=path) return self def request(self, method, path=None, params=None, data=None, **kwargs): # retrieve configuration ctx = self.configuration.pop() retry = self.client.configuration.get('http.retry', DEFAULT_HTTP_RETRY) max_retries = self.client.configuration.get('http.max_retries', DEFAULT_HTTP_MAX_RETRIES) retry_sleep = self.client.configuration.get('http.retry_sleep', DEFAULT_HTTP_RETRY_SLEEP) timeout = self.client.configuration.get('http.timeout', DEFAULT_HTTP_TIMEOUT) # build request if ctx.base_path and path: path = ctx.base_path + '/' + path elif ctx.base_path: path = ctx.base_path request = TraktRequest(self.client, method=method, path=path, params=params, data=data, **kwargs) prepared = request.prepare() # retrying requests on errors >= 500 response = None for i in range(max_retries + 1): if i > 0: log.warn('Retry # %s', i) try: response = self.session.send(prepared, timeout=timeout) except socket.gaierror as e: code, _ = e if code != 8: raise e log.warn('Encountered socket.gaierror (code: 8)') response = self._build_session().send(prepared, timeout=timeout) if not retry or response.status_code < 500: break log.warn('Continue retry since status is %s, waiting %s seconds', response.status_code, retry_sleep) time.sleep(retry_sleep) return response def get(self, path=None, params=None, data=None, **kwargs): return self.request('GET', path, params, data, **kwargs) def post(self, path=None, params=None, data=None, **kwargs): return self.request('POST', path, params, data, **kwargs) def delete(self, path=None, params=None, data=None, **kwargs): return self.request('DELETE', path, params, data, **kwargs) def _build_session(self): if self.session: log.info('Rebuilding session and connection pools...') # Build the connection pool self.session = requests.Session() self.session.mount('http://', HTTPAdapter(**self.adapter_kwargs)) self.session.mount('https://', HTTPAdapter(**self.adapter_kwargs)) return self.session
class HttpClient(object): def __init__(self, client, adapter_kwargs=None): self.client = client self.adapter_kwargs = adapter_kwargs or {} # Build client self.configuration = ContextStack() self.session = None self._build_session() def configure(self, path=None): self.configuration.push(base_path=path) return self def request(self, method, path=None, params=None, data=None, **kwargs): # retrieve configuration ctx = self.configuration.pop() retry = self.client.configuration.get('http.retry', False) max_retries = self.client.configuration.get('http.max_retries', 3) # build request if ctx.base_path and path: path = ctx.base_path + '/' + path elif ctx.base_path: path = ctx.base_path request = TraktRequest(self.client, method=method, path=path, params=params, data=data, **kwargs) prepared = request.prepare() # retrying requests on errors >= 500 response = None for i in range(max_retries + 1): if i > 0: log.warn('Retry # %s', i) try: response = self.session.send(prepared) except socket.gaierror, e: code, _ = e if code != 8: raise e log.warn('Encountered socket.gaierror (code: 8)') response = self._build_session().send(prepared) if not retry or response.status_code < 500: break log.warn('Continue retry since status is %s', response.status_code) time.sleep(5) return response