Skip to content

Commit

Permalink
Merge pull request #72 from CiscoDevNet/develop
Browse files Browse the repository at this point in the history
Merge Dev into Main
  • Loading branch information
jeroenwittock authored Jan 21, 2025
2 parents 721fc80 + 068cf79 commit d36e5b5
Showing 1 changed file with 60 additions and 12 deletions.
72 changes: 60 additions & 12 deletions plugins/httpapi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
import base64
from urllib import response
from urllib.parse import urlencode
import time

# provided for convenience, should be
LOGIN_PATH = "/api/fmc_platform/v1/auth/generatetoken"
REFRESH_PATH = "/api/fmc_platform/v1/auth/refreshtoken"


class InternalHttpClientError(Exception):
Expand All @@ -46,14 +48,23 @@ def __init__(self, host, login_url_path=None):
# maintained on login/logout
self._host = host
self._login_url_path = login_url_path or LOGIN_PATH
self.username = None
self.password = None
self.access_token = None
self.refresh_token = None

def send(self, url_path, data=None, method="GET", headers=None):
"""
Sends a request to the endpoint and returns the response body.
"""
if headers is not None and self.access_token is not None:
headers['X-auth-access-token'] = self.access_token

response = self._send_request(url_path, data, method, headers)
response_body = self._parse_response_body(response)
self._handle_error(response_body, response.status)
if self._handle_error(response_body, response.status) == 2:
# Retry send
self.send(url_path, data, method, headers)
# return the tuple just like connection.send
return response, response_body

Expand All @@ -69,12 +80,27 @@ def send_login(self, username, password):
'Authorization': 'Basic ' + encoded_creds_str
}
res = self._send_request(self._login_url_path, None, "POST", headers)
access_token = res.getheader("X-auth-access-token")
refresh_token = res.getheader("X-auth-refresh-token")

self.username = username
self.password = password
self.access_token = res.getheader("X-auth-access-token")
self.refresh_token = res.getheader("X-auth-refresh-token")
return {
'access_token': access_token,
'refresh_token': refresh_token
'access_token': self.access_token,
'refresh_token': self.refresh_token
}

def send_refresh_token(self):
headers = {
'Content-Type': 'application/json',
'X-auth-access-token': self.access_token,
'X-auth-refresh-token': self.refresh_token
}
response_body = self._send_request(REFRESH_PATH, None, "POST", headers)
self._handle_error(response_body, response.status)

self.access_token = response_body.getheader("X-auth-access-token")
self.refresh_token = response_body.getheader("X-auth-refresh-token")

def _send_request(self, url_path, data=None, method="GET", headers=None):
"""
Expand All @@ -84,7 +110,10 @@ def _send_request(self, url_path, data=None, method="GET", headers=None):
# ex:
# connection.send(url, data, method=http_method, headers=BASE_HEADERS)
method = method.upper()
conn = http.client.HTTPSConnection(self._host, timeout=360, context=ssl._create_unverified_context())

timeout = 60 if method == "POST" else 30
conn = http.client.HTTPSConnection(self._host, timeout=timeout, context=ssl._create_unverified_context())

conn.request(method, url_path, data, headers)
# response
response = conn.getresponse()
Expand All @@ -103,12 +132,31 @@ def _handle_error(self, response, status_code):
"""
Handles an error by parsing the response, and raising an error if found in response body.
"""
if 'error' in response:
err = response.get('error')
msg = err.get('data') or err.get('message') or iter_messages(err.get('messages'))
# raise ConnectionError(to_text(msg, errors='surrogate_then_replace'), code=code)
# raise InternalHttpClientError('FMC Error: {0}'.format(msg), status_code)
raise InternalHttpClientError(msg, status_code)
if 'error' not in response:
return 0

err = response.get('error')
msg = err.get('data') or err.get('message') or iter_messages(err.get('messages'))

if 'Access token invalid' in msg:
self.send_refresh_token()
return 2

if 'Invalid refresh token' in msg:
self.send_login(self.username, self.password)
return 2

if int(status_code) == 429:
retry_after = response.getheader("Retry-After")
try:
time.sleep(int(retry_after))
except (TypeError, ValueError):
time.sleep(30)
return 2

# raise ConnectionError(to_text(msg, errors='surrogate_then_replace'), code=code)
# raise InternalHttpClientError('FMC Error: {0}'.format(msg), status_code)
raise InternalHttpClientError(msg, status_code)


def iter_messages(messages):
Expand Down

0 comments on commit d36e5b5

Please sign in to comment.