Page MenuHomePhabricator

No OneTemporary

diff --git a/pushserver/resources/utils.py b/pushserver/resources/utils.py
index df4a4f1..d38e573 100644
--- a/pushserver/resources/utils.py
+++ b/pushserver/resources/utils.py
@@ -1,360 +1,391 @@
import hashlib
import json
import logging
import socket
import ssl
import time
from ipaddress import ip_address
from datetime import datetime
__all__ = ['callid_to_uuid', 'fix_non_serializable_types', 'resources_available', 'ssl_cert', 'try_again', 'check_host',
'log_event', 'fix_device_id', 'fix_platform_name', 'log_incoming_request']
def callid_to_uuid(call_id: str) -> str:
"""
Generate a UUIDv4 from a callId.
UUIDv4 format: five segments of seemingly random hex data,
beginning with eight hex characters, followed by three
four-character strings, then 12 characters at the end.
These segments are separated by a “-”.
:param call_id: `str` Globally unique identifier of a call.
:return: a str with a uuidv4.
"""
hexa = hashlib.md5(call_id.encode()).hexdigest()
uuidv4 = '%s-%s-%s-%s-%s' % \
(hexa[:8], hexa[8:12], hexa[12:16], hexa[16:20], hexa[20:])
return uuidv4
def fix_non_serializable_types(obj):
"""
Converts a non serializable object in an appropriate one,
if it is possible and in a recursive way.
If not, return the str 'No JSON Serializable object'
:param obj: obj to convert
"""
if isinstance(obj, bytes):
string = obj.decode()
return fix_non_serializable_types(string)
elif isinstance(obj, dict):
return {
fix_non_serializable_types(k): fix_non_serializable_types(v)
for k, v in obj.items()
}
elif isinstance(obj, (tuple, list)):
return [fix_non_serializable_types(elem) for elem in obj]
elif isinstance(obj, str):
try:
dict_obj = json.loads(obj)
return fix_non_serializable_types(dict_obj)
except json.decoder.JSONDecodeError:
return obj
elif isinstance(obj, (bool, int, float)):
return obj
else:
return
def resources_available(host: str, port: int) -> bool:
"""
Check if a pair ip, port is available for a connection
:param: `str` host
:param: `int` port
:return: a `bool` according to the test result.
"""
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if not host or not port:
return None
try:
serversocket.bind((host, port))
serversocket.close()
return True
except OSError:
return False
def ssl_cert(cert_file: str, key_file=None) -> bool:
"""
Check if a ssl certificate is valid.
:param cert_file: `str` path to certificate file
:param key_file: `str` path to key file
:return: `bool` True for a valid certificate.
"""
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
try:
ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file)
return True
except (ssl.SSLError, NotADirectoryError, TypeError):
return False
def try_again(timer: int, host: str, port: int,
start_error: str, loggers: dict) -> None:
"""
Sleep for a specific time and send log messages
in case resources would not be available to start the app.
:param timer: `int` time in seconds to wait (30 = DHCP delay)
:param host: `str` IP address where app is trying to run
:param port: `int` Host where app is trying to run
:param start_error: `stṛ` Error msg to show in log.
:param loggers: global logging instances to write messages (params.loggers)
"""
timer = timer # seconds, 30 for dhcp delay.
level = 'error'
msg = f"[can not init] on {host}:{port} - resources are not available"
log_event(msg=start_error, level=level, loggers=loggers)
log_event(msg=msg, level=level, loggers=loggers)
msg = f'Server will try again in {timer} seconds'
log_event(msg=msg, level=level, loggers=loggers)
time.sleep(timer)
def check_host(host, allowed_hosts) -> bool:
"""
Check if a host is in allowed_hosts
:param host: `str` to check
:return: `bool`
"""
if not allowed_hosts:
return True
for subnet in allowed_hosts:
if ip_address(host) in subnet:
return True
return False
def log_event(loggers: dict, msg: str, level: str = 'deb',
to_file: bool = False) -> None:
"""
Write log messages into log file and in journal if specified.
:param loggers: `dict` global logging instances to write messages (params.loggers)
:param msg: `str` message to write
:param level: `str` info, error, deb or warn
:param to_file: `bool` write just in file if True
"""
if loggers.get('to_file'):
logger = loggers.get('to_file')
else:
logger = loggers.get('to_journal')
if len(logger.handlers) > 1:
logger.handlers = [logger.handlers[0]]
msg = f'{datetime.now()} {msg}'
if level == 'info':
logger.setLevel(logging.INFO)
logger.info(msg)
elif level == 'error':
logger.setLevel(logging.ERROR)
logger.error(msg)
elif level == 'warn':
logger.setLevel(logging.WARNING)
logger.warning(msg)
elif level in ('deb', 'debug'):
logger.setLevel(logging.DEBUG)
logger.debug(msg)
def fix_device_id(device_id_to_fix: str) -> str:
"""
Remove special characters from uuid
:param device_id_to_fix: `str` uuid with special characters.
:return: a `str` with fixed uuid.
"""
if '>' in device_id_to_fix:
uuid = device_id_to_fix.split(':')[-1].replace('>', '')
elif ':' in device_id_to_fix:
uuid = device_id_to_fix.split(':')[-1]
else:
uuid = device_id_to_fix
device_id = uuid
return device_id
def fix_platform_name(platform: str) -> str:
"""
Fix platform name in case its value is 'android' or 'ios',
replacing it for 'firebase' and 'apple'
:param platform: `str` name of platform
:return: a `str` with fixed name.
"""
if platform in ('firebase', 'android'):
return 'firebase'
elif platform in ('apple', 'ios'):
return 'apple'
else:
return platform
def fix_payload(body: dict) -> dict:
payload = {}
for item in body.keys():
value = body[item]
if item in ('sip_to', 'sip_from'):
item = item.split('_')[1]
else:
item = item.replace('_', '-')
payload[item] = value
return payload
def log_add_request(task: str, host: str, loggers: dict,
request_id: str = None, body: dict = None,
error_msg: str = None) -> None:
"""
Send log messages according to type of event.
:param task: `str` type of event to log, can be
'log_request', 'log_success' or 'log_failure'
:param host: `str` client host where request comes from
:param loggers: `dict` global logging instances to write messages (params.loggers)
:param request_id: `str` request ID generated on request
:param body: `dict` body of request
:param error_msg: `str` to show in log
"""
app_id = body.get('app_id')
platform = body.get('platform')
platform = platform if platform else ''
device_id = body.get('device_id')
device_id = fix_device_id(device_id) if device_id else None
if task == 'log_request':
payload = fix_payload(body)
level = 'info'
msg = f'{host} - Add Token - {request_id}: ' \
f'{payload}'
log_event(msg=msg, level=level, loggers=loggers, to_file=True)
elif task == 'log_success':
payload = fix_payload(body)
msg = f'{host} - Add Token - Response {request_id}: ' \
f'{payload}'
level = 'info'
log_event(msg=msg, level=level, loggers=loggers, to_file=True)
elif task == 'log_failure':
level = 'error'
resp = error_msg
print(resp)
msg = f'{host} - Add Token Failed - Response {request_id}: ' \
f'{resp}'
log_event(loggers=loggers, msg=msg, level=level, to_file=True)
def log_remove_request(task: str, host: str, loggers: dict,
request_id: str = None, body: dict = None,
error_msg: str = None) -> None:
"""
Send log messages according to type of event.
:param task: `str` type of event to log, can be
'log_request', 'log_success' or 'log_failure'
:param host: `str` client host where request comes from
:param loggers: `dict` global logging instances to write messages (params.loggers)
:param request_id: `str` request ID generated on request
:param body: `dict` body of request
:param error_msg: `str` to show in log
"""
app_id = body.get('app_id')
platform = body.get('platform')
platform = platform if platform else ''
device_id = body.get('device_id')
device_id = fix_device_id(device_id) if device_id else None
if task == 'log_request':
payload = fix_payload(body)
level = 'info'
msg = f'{host} - Remove Token - {request_id}: ' \
f'{payload}'
log_event(msg=msg, level=level, loggers=loggers, to_file=True)
elif task == 'log_success':
payload = fix_payload(body)
msg = f'{host} - Remove Token - Response {request_id}: ' \
f'{payload}'
level = 'info'
log_event(msg=msg, level=level, loggers=loggers, to_file=True)
elif task == 'log_failure':
level = 'error'
resp = error_msg
msg = f'{host} - Remove Token Failed - Response {request_id}: ' \
f'{resp}'
log_event(loggers=loggers, msg=msg, level=level, to_file=True)
+def log_push_request(task: str, host: str, loggers: dict,
+ request_id: str = None, body: dict = None,
+ error_msg: str = None) -> None:
+ """
+ Send log messages according to type of event.
+ :param task: `str` type of event to log, can be
+ 'log_request', 'log_success' or 'log_failure'
+ :param host: `str` client host where request comes from
+ :param loggers: `dict` global logging instances to write messages (params.loggers)
+ :param request_id: `str` request ID generated on request
+ :param body: `dict` body of request
+ :param error_msg: `str` to show in log
+ """
+ sip_to = body.get('sip_to')
+ event = body.get('event')
+
+ if task == 'log_request':
+ payload = fix_payload(body)
+ level = 'info'
+ msg = f'{host} - Push - {request_id}: ' \
+ f'{event} for {sip_to} ' \
+ f': {payload}'
+ log_event(msg=msg, level=level, loggers=loggers, to_file=True)
+
+ elif task == 'log_failure':
+ level = 'error'
+ resp = error_msg
+ msg = f'{host} - Push Failed - Response {request_id}: ' \
+ f'{resp}'
+ log_event(loggers=loggers, msg=msg, level=level, to_file=True)
+
def log_incoming_request(task: str, host: str, loggers: dict,
request_id: str = None, body: dict = None,
error_msg: str = None) -> None:
"""
Send log messages according to type of event.
:param task: `str` type of event to log, can be
'log_request', 'log_success' or 'log_failure'
:param host: `str` client host where request comes from
:param loggers: `dict` global logging instances to write messages (params.loggers)
:param request_id: `str` request ID generated on request
:param body: `dict` body of request
:param error_msg: `str` to show in log
"""
app_id = body.get('app_id')
platform = body.get('platform')
platform = platform if platform else ''
sip_to = body.get('sip_to')
device_id = body.get('device_id')
device_id = fix_device_id(device_id) if device_id else None
event = body.get('event')
if task == 'log_request':
payload = fix_payload(body)
level = 'info'
if sip_to:
if device_id:
msg = f'incoming {platform.title()} request {request_id}: ' \
f'{event} for {sip_to} using' \
f' device {device_id} from {host}: {payload}'
else:
msg = f'incoming {platform.title()} request {request_id}: ' \
f'{event} for {sip_to} ' \
f'from {host}: {payload}'
elif device_id:
msg = f'incoming {platform.title()} request {request_id}: ' \
f'{event} using' \
f' device {device_id} from {host}: {payload}'
else:
msg = f'incoming {platform.title()} request {request_id}: ' \
f' from {host}: {payload}'
log_event(msg=msg, level=level, loggers=loggers, to_file=True)
elif task == 'log_success':
msg = f'incoming {platform.title()} response for {request_id}: ' \
f'push accepted'
level = 'info'
log_event(msg=msg, level=level, loggers=loggers, to_file=True)
elif task == 'log_failure':
level = 'error'
resp = error_msg
msg = f'incoming {platform.title()} from {host} response for {request_id}, ' \
f'push rejected: {resp}'
log_event(loggers=loggers, msg=msg, level=level, to_file=True)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 5:51 AM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3408181
Default Alt Text
(13 KB)

Event Timeline