Skip to content

Commit

Permalink
Documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
bontiv committed Sep 10, 2018
1 parent 1d1146b commit 558aa4b
Show file tree
Hide file tree
Showing 12 changed files with 351 additions and 26 deletions.
2 changes: 2 additions & 0 deletions docs/apidoc/upnp.HTTP.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ upnp.HTTP module

.. automodule:: upnp.HTTP
:members:
:undoc-members:
:show-inheritance:
2 changes: 2 additions & 0 deletions docs/apidoc/upnp.Objects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ upnp.Objects module

.. automodule:: upnp.Objects
:members:
:undoc-members:
:show-inheritance:
2 changes: 2 additions & 0 deletions docs/apidoc/upnp.SSDP.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ upnp.SSDP module

.. automodule:: upnp.SSDP
:members:
:undoc-members:
:show-inheritance:
2 changes: 2 additions & 0 deletions docs/apidoc/upnp.UPnP.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ upnp.UPnP module

.. automodule:: upnp.UPnP
:members:
:undoc-members:
:show-inheritance:
2 changes: 2 additions & 0 deletions docs/apidoc/upnp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ Module contents

.. automodule:: upnp
:members:
:undoc-members:
:show-inheritance:
3 changes: 2 additions & 1 deletion docs/make.bat
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ if errorlevel 9009 (
exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:html
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -a
goto end

:help
Expand Down
14 changes: 10 additions & 4 deletions docs/quickstart/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,32 @@ Quickstart

.. toctree::
:maxdepth: 2
:hidden:

dict
attribute
asyncio


IoT-UPnP require python 3.x. It use the following modules:
* asyncio: for the main event loop
* ssdp: base library for SSDP (a component of UPnP)
* netifaces: Network interfaces discovery (to retrieve IPs)

* asyncio: for the main event loop
* ssdp: base library for SSDP (a component of UPnP)
* netifaces: Network interfaces discovery (to retrieve IPs)

They are tree important objects:

.. autoclass:: upnp.Announcer

See :class:`upnp.UPnP.Announcer`

.. autoclass:: upnp.Device

See :class:`upnp.Objects.Device`

.. autoclass:: upnp.Service

See :class:`upnp.Objects.Service`

All objects can be set with theirs attributes or by passing a dict on the contructor.

Goal
Expand Down
168 changes: 150 additions & 18 deletions upnp/HTTP.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,48 @@
import asyncio
from socket import gethostname

class HttpResponder(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('Connection from {}'.format(peername))
self.transport = transport

def connection_lost(self, exc):
print('Connection lost')

def data_received(self, data):
message = data.decode()
print('Data received: {!r}'.format(message))
print('Close the client socket')
#self.transport.close()

def eof_received(self):
print('End data')

class HttpRequest:
"""
HTTP Request informations
"""
def __init__(self, method, path, version, headers):
"""
Initiate a request
:param method: HTTP request method
:type method: str
:param path: The requested path
:type path: str
:param version: HTTP version
:type version: str
:param headers: Dictionary of request headers
:type headers: {str:str}
"""
self.method = method
self.path = path
self.version = version
self.headers = headers

def pprint(self):
"""
Print human readable request
"""
print('{} {} {}'.format(self.method, self.path, self.version))
for h in self.headers:
print (' {}: {}'.format(h, self.headers[h]))
print()

class HttpAnswer:
"""
Class to construct an HTTP response
"""
def __init__(self, request):
"""
Initiate a response
:param request: Origin request
:type request: upnp.HTTP.HttpRequest
"""
self.request = request
self.statusCode = 200
self.statusText = 'OK'
Expand All @@ -46,6 +55,12 @@ def __init__(self, request):
self.data = None

def write(self, writer):
"""
Send the response
:param writer: Writer to send pakcet
:type writer: asyncio.StreamWriter
"""
writer.write('{} {} {}\r\n'.format(self.version, self.statusCode, self.statusText).encode('latin1'))
for h in self.headers:
writer.write('{}: {}\r\n'.format(h, self.headers[h]).encode('latin1'))
Expand All @@ -55,26 +70,57 @@ def write(self, writer):
writer.write(b'\r\n')

def pprint(self):
"""
Show the packet as human readable
"""
print('{} {} {}'.format(self.version, self.statusCode, self.statusText))
for h in self.headers:
print(' {}: {}'.format(h, self.headers[h]))
print()

def execute(self):
"""
Need to be overrided by subclass. It's the execute process.
"""
pass

class ServerErrorAnswer(HttpAnswer):
"""
HTTP error response
"""
def execute(self):
"""
Prepare the response
"""
self.statusCode = 500
self.statusText = 'Internal Server Error'
self.data = '<html><body><h1>Internal Server Error</h1><p>An internal server error. See logs.</p></body></html>'

class DescriptionAnswer(HttpAnswer):
"""
HTTP success, describe a device (XML)
"""

def __init__(self, request, upnp):
"""
Initiate a device description
:param request: Origin request
:type request: upnp.HTTP.HttpRequest
:param upnp: An UPnP configuration to describe
:type upnp: upnp.UPnP.Announcer
"""
super(DescriptionAnswer, self).__init__(request)
self.upnp = upnp

def describeDevice(self, device):
"""
Add a device description to the answer
:param device: Device to describe
:type device: upnp.Objects.Device
"""

self.data += """
<device>
<deviceType>{DEVICE.deviceType}</deviceType>
Expand Down Expand Up @@ -109,9 +155,22 @@ def describeDevice(self, device):


def describeIcon(self, icon):
"""
Add an icon description to the answer
:param icon: Icon to describe
:type icon: upnp.Objects.Icon
"""
pass

def describeService(self, service):
"""
Add a service description to the answer
:param service: Service to describe
:type service: upnp.Objects.Service
"""

self.data += """
<service>
<serviceType>{SERVICE.serviceType}</serviceType>
Expand All @@ -123,6 +182,9 @@ def describeService(self, service):
""".format(SERVICE=service)

def execute(self):
"""
Prepare the description answer
"""

self.headers['Content-Type'] = 'application/xml; charset=utf-8'
self.URL = 'http://{}'.format(self.request.headers['host'])
Expand All @@ -142,11 +204,27 @@ def execute(self):
"""

class ScpdAnswer(HttpAnswer):
"""
HTTP success, Describe a service API
"""
def __init__(self, request, upnp):
"""
Initiate services to describe from the root device
:param request: Origin request
:type request: upnp.HTTP.HttpRequest
:param upnp: An UPnP configuration to describe
:type upnp: upnp.UPnP.Announcer
"""

super(DescriptionAnswer, self).__init__(request)
self.upnp = upnp

def execute(self):
"""
Prepare the answer
"""

self.headers['Content-Type'] = 'application/xml; charset=utf-8'
URL = 'http://{}'.format(self.request.headers['host'])
Expand All @@ -163,10 +241,29 @@ def execute(self):
""".format(UUID=UUID, URL=URL, CONFIGID=self.upnp.configId)

class HttpServer:
"""
class to handle asyncio events on HTTP service
"""

def __init__(self, config):
"""
Initiate the HTTP server
:param config: HTTP configuration
:type config: upnp.HTTP.HTTP
"""
self.config = config

def InConnection(self, reader, writer):
"""
A new incomming connection
:param reader: Request input
:type reader: asyncio.StreamReader
:param writer: Stream to answer
:type writer: asyncio.StreamWriter
"""

header = yield from reader.readline()
cheaders = header.decode('latin1').strip()
method, path, vers = cheaders.split(' ')
Expand All @@ -189,6 +286,15 @@ def InConnection(self, reader, writer):
writer.close()

def HttpRouting(self, request):
"""
A simple routing by path for incomming requests
:param request: The incomming request
:type request: upnp.HTTP.HttpRequest
:return: The answer to execute
:rtype: upnp.HTTP.HttpAnswer
"""

if request.path == '/descr.xml':
ans = DescriptionAnswer(request, self.config.annoncer)
elif request.path == '/scpd.xml':
Expand All @@ -198,7 +304,22 @@ def HttpRouting(self, request):
return ans

class HTTP:
"""
The main HTTP server class
"""

def __init__(self, annoncer, port, netbind):
"""
Initiate an HTTP Server
:param annoncer: The UPnP configuration
:type annoncer: upnp.UPnP.Announcer
:param port: HTTP port
:type port: int
:param netbind: Interface address to bind
:type netbind: str
"""

self.port = port
self.netbind = netbind
self.server = None
Expand All @@ -209,8 +330,19 @@ def __init__(self, annoncer, port, netbind):
self.netbind = None

def initLoop(self, loop):
"""
Add HTTP handlers on the asyncio loop
:param loop: Loop to use
:type loop: asyncio.AbstractEventLoop
"""

self.server = asyncio.start_server(self.http_server.InConnection, port=self.port, host=self.netbind)
self.httploop = loop.run_until_complete(self.server)

def dispose(self):
"""
Close HTTP handling
"""

pass
4 changes: 4 additions & 0 deletions upnp/Objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ class Device(_BaseObj):
def __init__(self, obj=None):
"""
Device object initialisation
:param obj: A dict with attributes
:type obj: dict or None
"""
self.services = []
self.devices = []
Expand All @@ -42,6 +44,7 @@ def __init__(self, obj=None):
def addService(self, service):
"""
Add a service on this device
:param service: The service to add
:type service: upnp.Service
"""
Expand All @@ -50,6 +53,7 @@ def addService(self, service):
def addDevice(self, device):
"""
Add an embedded device
:param device: The embedded device to add
:type device: upnp.Device
"""
Expand Down
Loading

0 comments on commit 558aa4b

Please sign in to comment.