ControlSystem-SSDP/Server/SSDP_Response.py

148 lines
4.4 KiB
Python

import socket
import struct
import threading
import queue
import time
import datetime
import uuid
import random
class ssdp_response():
SSDP_GROUP = '239.255.255.250'
SSDP_PORT = 1900
TEMPLATE ="""HTTP/1.1 200 OK
CACHE-CONTROL: max-age={}
DATE: {}
EXT:
LOCATION: {}
SERVER: {}
ST: {}
USN: {}
BOOTID.UPNP.ORG: {}
"""
MAX_AGE = 500
LOCATION = ""
SERVER = "PYTHON/3 UPnP/1.1 CONTROL_SERVER/1.0.0"
USN = uuid.uuid4()
BOOTID = 0
queues = {}
queues_lock = threading.Lock()
requests = queue.Queue()
filters = {"HEAD":"M-SEARCH"}
def __init__(self, filter, location=None):
self.filters.update(filter)
if not location:
self.LOCATION = socket.gethostbyname_ex(socket.getfqdn())[2]
else:
self.LOCATION = location
def start_listener(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("", self.SSDP_PORT))
multicast_request = struct.pack("4sl", socket.inet_aton(self.SSDP_GROUP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, multicast_request)
#start listen and respond thread
while True:
try:
data,addr = sock.recvfrom(1024)
if addr in self.queues:
self.queues[addr].put(data)
else:
q = queue.Queue()
q.put(data)
self.queues_lock.acquire()
self.queues[addr] = q
self.queues_lock.release()
except Exception as e:
print(e)
print("recv error")
def filter(self, request):
ret = True
for k in self.filters.keys():
val = request.get(k)
if not val:
ret = False
break
else:
if self.filters[k] not in val:
ret = False
break
return ret
def parse_request(self):
requests_buffer = {}
while True:
time.sleep(1)
self.queues_lock.acquire()
timeouts = []
for k, v in self.queues.items():
try:
data = v.get(timeout=5)
string = data.decode()
lines = string.splitlines()
for line in lines:
val = line.split(':', 1)
if len(val) == 1:
if "M-SEARCH" in val[0]:
requests_buffer[k] = {}
requests_buffer[k]["HEAD"] = val[0]
else:
requests_buffer[k]["CLIENT"] = k[0]
if self.filter(requests_buffer[k]):
print(requests_buffer[k])
self.requests.put(requests_buffer.pop(k))
else:
requests_buffer[k][val[0]] = val[1]
except Exception as e:
print(e)
print("no data")
timeouts.append(k)
for k in timeouts:
del self.queues[k]
self.queues_lock.release()
def construct_response(self, request):
response = self.TEMPLATE.format(
self.MAX_AGE,
datetime.datetime.now().replace(microsecond=0).isoformat(),
self.LOCATION,
self.SERVER,
request.get("ST"),
self.USN,
self.BOOTID)
self.BOOTID = self.BOOTID + 1
return response
def send_response(self):
while True:
request = self.requests.get()
time.sleep(random.randrange(int(request.get("MX"))))
response = self.construct_response(request)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
address = (request.get("CLIENT"), self.SSDP_PORT)
print(response)
sock.connect(address)
sock.sendall(response.encode())
sock.close();
s = ssdp_response({"ST":"service:control-server"}, "192.168.0.184")
t = threading.Thread(target=s.start_listener)
t.start()
t2 = threading.Thread(target=s.parse_request)
t2.start()
s.send_response()