#!/usr/bin/env python3 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Class holds default settings for json requests - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - import logging import sys import os from pprint import pformat, PrettyPrinter import urllib from urllib import request import json if sys.version_info[0] != 3: print("This script requires Python 3") exit() sys.path.append(os.path.join(os.path.abspath(__file__ + "../../../../"))) debug_printer = PrettyPrinter(indent=2) class LFRequest: Default_Base_URL = "http://localhost:8080" No_Data = {'No Data':0} requested_url = "" post_data = No_Data default_headers = { 'Accept': 'application/json'} proxies = None logger = logging.getLogger(__name__) def __init__(self, url=None, uri=None, proxies_=None, debug_=False, die_on_error_=False): self.debug = debug_ self.die_on_error = die_on_error_ self.error_list = [] # please see this discussion on ProxyHandlers: # https://docs.python.org/3/library/urllib.request.html#urllib.request.ProxyHandler # but this makes much more sense: # https://gist.github.com/aleiphoenix/4159510 # if debug_: # if proxies_ is None: # print("LFRequest_init_: no proxies_") # else: # print("LFRequest: proxies_: ") # pprint.pprint(proxies_) if (proxies_ is not None) and (len(proxies_) > 0): if ("http" not in proxies_) and ("https" not in proxies_): raise ValueError("Neither http or https set in proxy definitions. Expects proxy={'http':, 'https':, }") self.proxies = proxies_ # if debug_: # if self.proxies is None: # print("LFRequest_init_: no proxies") # else: # print("LFRequest: proxies: ") # pprint.pprint(self.proxies) if not url.startswith("http://") and not url.startswith("https://"): self.logger.warning("No http:// or https:// found, prepending http:// to "+url) url = "http://" + url if uri is not None: if not url.endswith('/') and not uri.startswith('/'): url += '/' self.requested_url = url + uri else: self.requested_url = url if self.requested_url is None: raise Exception("Bad LFRequest of url[%s] uri[%s] -> None" % (url, uri)) if self.requested_url.find('//'): protopos = self.requested_url.find("://") self.requested_url = self.requested_url[:protopos + 2] + self.requested_url[protopos + 2:].replace("//", "/") # finding '#' prolly indicates a macvlan (eth1#0) # finding ' ' prolly indicates a field name that should imply %20 if (self.requested_url.find('#') >= 1): self.requested_url = self.requested_url.replace('#', '%23') if (self.requested_url.find(' ') >= 1): self.requested_url = self.requested_url.replace(' ', '+') self.logger.debug("new LFRequest[%s]" % self.requested_url ) # request first url on stack def formPost(self, show_error=True, debug=False, die_on_error_=False): return self.form_post(show_error=show_error, debug=debug, die_on_error_=die_on_error_) def form_post(self, show_error=True, debug=False, die_on_error_=False): if self.die_on_error: die_on_error_ = True if (debug == False) and (self.debug == True): debug = True responses = [] urlenc_data = "" # https://stackoverflow.com/a/59635684/11014343 if (self.proxies is not None) and (len(self.proxies) > 0): # https://stackoverflow.com/a/59635684/11014343 opener = request.build_opener(request.ProxyHandler(self.proxies)) request.install_opener(opener) self.logger.debug("formPost: url: "+self.requested_url) if ((self.post_data != None) and (self.post_data is not self.No_Data)): urlenc_data = urllib.parse.urlencode(self.post_data).encode("utf-8") self.logger.debug("formPost: data looks like:" + str(urlenc_data)) self.logger.debug("formPost: url: "+self.requested_url) myrequest = request.Request(url=self.requested_url, data=urlenc_data, headers=self.default_headers) else: myrequest = request.Request(url=self.requested_url, headers=self.default_headers) self.logger.error("No data for this formPost?") myrequest.headers['Content-type'] = 'application/x-www-form-urlencoded' resp = '' try: resp = urllib.request.urlopen(myrequest) responses.append(resp) return responses[0] except urllib.error.HTTPError as error: print_diagnostics(url_=self.requested_url, request_=myrequest, responses_=responses, error_=error, error_list_=self.error_list, debug_=debug) except urllib.error.URLError as uerror: print_diagnostics(url_=self.requested_url, request_=myrequest, responses_=responses, error_=uerror, error_list_=self.error_list, debug_=debug) if (die_on_error_ == True) or (self.die_on_error == True): exit(1) return None def jsonPost(self, show_error=True, debug=False, die_on_error_=False, response_json_list_=None): return self.json_post(show_error=show_error, debug=debug, die_on_error_=die_on_error_, response_json_list_=response_json_list_) def json_post(self, show_error=True, debug=False, die_on_error_=False, response_json_list_=None, method_='POST'): if (debug == False) and (self.debug == True): debug = True if self.die_on_error: die_on_error_ = True responses = [] if (self.proxies is not None) and (len(self.proxies) > 0): opener = urllib.request.build_opener(request.ProxyHandler(self.proxies)) urllib.request.install_opener(opener) if ((self.post_data != None) and (self.post_data is not self.No_Data)): myrequest = request.Request(url=self.requested_url, method=method_, data=json.dumps(self.post_data).encode("utf-8"), headers=self.default_headers) else: myrequest = request.Request(url=self.requested_url, headers=self.default_headers) self.logger.error("No data for this jsonPost?") myrequest.headers['Content-type'] = 'application/json' # https://stackoverflow.com/a/59635684/11014343 try: resp = urllib.request.urlopen(myrequest) resp_data = resp.read().decode('utf-8') if (debug and die_on_error_): self.logger.debug("----- LFRequest::json_post:128 debug: --------------------------------------------") self.logger.debug("URL: <%s> status: %d "% (self.requested_url, resp.status)) if resp.status != 200: self.logger.debug(pformat(resp.getheaders())) self.logger.debug("----- resp_data:128 -------------------------------------------------") self.logger.debug(resp_data) self.logger.debug("-------------------------------------------------") responses.append(resp) if response_json_list_ is not None: if type(response_json_list_) is not list: raise ValueError("reponse_json_list_ needs to be type list") j = json.loads(resp_data) if debug: self.logger.debug("----- LFRequest::json_post:140 debug: --------------------------------------------") self.logger.debug(pformat(j)) self.logger.debug("-------------------------------------------------") response_json_list_.append(j) return responses[0] except urllib.error.HTTPError as error: print_diagnostics(url_=self.requested_url, request_=myrequest, responses_=responses, error_=error, debug_=debug) except urllib.error.URLError as uerror: print_diagnostics(url_=self.requested_url, request_=myrequest, responses_=responses, error_=uerror, debug_=debug) if die_on_error_ == True: exit(1) return None def json_put(self, show_error=True, debug=False, die_on_error_=False, response_json_list_=None): return self.json_post(show_error=show_error, debug=debug, die_on_error_=die_on_error_, response_json_list_=response_json_list_, method_='PUT') def json_delete(self, show_error=True, debug=False, die_on_error_=False, response_json_list_=None): return self.get_as_json(method_='DELETE') def get(self, method_='GET'): if self.debug: self.logger.debug("LFUtils.get: url: "+self.requested_url) # https://stackoverflow.com/a/59635684/11014343 if (self.proxies is not None) and (len(self.proxies) > 0): opener = request.build_opener(request.ProxyHandler(self.proxies)) #opener = urllib.request.build_opener(myrequest.ProxyHandler(self.proxies)) request.install_opener(opener) myrequest = request.Request(url=self.requested_url, headers=self.default_headers, method=method_) myresponses = [] try: myresponses.append(request.urlopen(myrequest)) return myresponses[0] except urllib.error.HTTPError as error: print_diagnostics(url_=self.requested_url, request_=myrequest, responses_=myresponses, error_=error, error_list_=self.error_list, debug_=self.debug) except urllib.error.URLError as uerror: print_diagnostics(url_=self.requested_url, request_=myrequest, responses_=myresponses, error_=uerror, error_list_=self.error_list, debug_=self.debug) if self.die_on_error is True: exit(1) return None def getAsJson(self): return self.get_as_json() def get_as_json(self, method_='GET'): responses = list() responses.append(self.get(method_=method_)) if len(responses) < 1: if self.debug and self.has_errors(): self.print_errors() return None if responses[0] is None: self.logger.debug("No response from "+self.requested_url) return None json_data = json.loads(responses[0].read().decode('utf-8')) return json_data def addPostData(self, data): self.add_post_data(data=data) def add_post_data(self, data): """ TODO: this is a setter and should be named 'set_post_data' :param data: dictionary of parameters for post :return: nothing """ self.post_data = data def has_errors(self): return (True, False)[len(self.error_list)>0] def print_errors(self): if not self.has_errors: self.logger.debug("---------- no errors ----------") return for err in self.error_list: self.logger.error("error: %s" % err) def plain_get(url_=None, debug_=False, die_on_error_=False, proxies_=None): """ This static method does not respect LFRequest.proxy, it is not set in scope here :param url_: :param debug_: :param die_on_error_: :return: """ myrequest = request.Request(url=url_) myresponses = [] try: if (proxies_ is not None) and (len(proxies_) > 0): # https://stackoverflow.com/a/59635684/11014343 opener = myrequest.build_opener(myrequest.ProxyHandler(proxies_)) myrequest.install_opener(opener) myresponses.append(request.urlopen(myrequest)) return myresponses[0] except urllib.error.HTTPError as error: print_diagnostics(url_=url_, request_=request, responses_=myresponses, error_=error, debug_=debug_) except urllib.error.URLError as uerror: print_diagnostics(url_=url_, request_=request, responses_=myresponses, error_=uerror, debug_=debug_) if die_on_error_ == True: exit(1) return None def print_diagnostics(url_=None, request_=None, responses_=None, error_=None, error_list_=None, debug_=False): logger = logging.getLogger(__name__) #logger.error("LFRequest::print_diagnostics: error_.__class__: %s"%error_.__class__) #logger.error(pformat(error_)) if url_ is None: logger.warning("WARNING LFRequest::print_diagnostics: url_ is None") if request_ is None: logger.warning("WARNING LFRequest::print_diagnostics: request_ is None") if error_ is None: logger.warning("WARNING LFRequest::print_diagnostics: error_ is None") method = 'NA' if hasattr(request_, 'method'): method = request_.method err_code = 0 err_reason = 'NA' err_headers = [] err_full_url = url_ if hasattr(error_, 'code'): err_code = error_.code if hasattr(error_, 'reason'): err_reason = error_.reason if hasattr(error_, 'headers'): err_headers = error_.headers if hasattr(error_, 'get_full_url'): err_full_url = error_.get_full_url() xerrors = [] if err_code == 404: xerrors.append("[%s HTTP %s] <%s> : %s" % (method, err_code, err_full_url, err_reason)) else: if len(err_headers) > 0: for headername in sorted(err_headers.keys()): if headername.startswith("X-Error-"): xerrors.append("%s: %s" % (headername, err_headers.get(headername))) if len(xerrors) > 0: logger.error(" = = LANforge Error Messages = =") logger.error(" = = URL: %s" % err_full_url) for xerr in xerrors: logger.error(xerr) if (error_list_ is not None) and isinstance(error_list_, list): error_list_.append(xerr) logger.error(" = = = = = = = = = = = = = = = =") if error_.__class__ is urllib.error.HTTPError: logger.debug("----- LFRequest: HTTPError: --------------------------------------------") logger.debug("%s <%s> HTTP %s: %s" % (method, err_full_url, err_code, err_reason)) if err_code == 404: if (error_list_ is not None) and isinstance(error_list_, list): error_list_.append("[%s HTTP %s] <%s> : %s" % (method, err_code, err_full_url, err_reason)) else: logger.debug(" Content-type:[%s] Accept[%s]" % (request_.get_header('Content-type'), request_.get_header('Accept'))) if hasattr(request_, "data") and (request_.data is not None): logger.debug(" Data:") logger.debug(debug_printer.pformat(request_.data)) elif debug_: logger.debug(" ") if len(err_headers) > 0: # the HTTPError is of type HTTPMessage a subclass of email.message logger.debug(" Response Headers: ") for headername in sorted(err_headers.keys()): logger.debug(" %s: %s" % (headername, err_headers.get(headername))) if len(responses_) > 0: logger.debug("----- Response: --------------------------------------------------------") logger.debug(debug_printer.pformat(responses_[0].reason)) logger.debug("------------------------------------------------------------------------") return if error_.__class__ is urllib.error.URLError: logger.error("----- LFRequest: URLError: ---------------------------------------------") logger.error("%s <%s> HTTP %s: %s" % (method, err_full_url, err_code, err_reason)) logger.error("------------------------------------------------------------------------") # ~LFRequest