コード例 #1
0
def create_web_server():
    server = MicroWebSrv(webPath='www/')
    server.MaxWebSocketRecvLen = 256
    server.WebSocketThreaded = False
    server.AcceptWebSocketCallback = acceptWebSocketCallback
    server.Start(threaded=True)
    return server
コード例 #2
0
def _httpHandlerTestPost(httpClient, httpResponse):
    formData = httpClient.ReadRequestPostedFormData()
    firstname = formData["firstname"]
    lastname = formData["lastname"]
    content = """\
	<!DOCTYPE html>
	<html lang=en>
		<head>
			<meta charset="UTF-8" />
            <title>TEST POST</title>
        </head>
        <body>
            <h1>TEST POST</h1>
            Firstname = %s<br />
            Lastname = %s<br />
        </body>
    </html>
	""" % (MicroWebSrv.HTMLEscape(firstname), MicroWebSrv.HTMLEscape(lastname))
    httpResponse.WriteResponseOk(headers=None,
                                 contentType="text/html",
                                 contentCharset="UTF-8",
                                 content=content)
コード例 #3
0
def create_web_server(https=False):
    if https:
        server = MicroWebSrv(webPath='www/',
                             port=8443,
                             sslOptions={
                                 'key': mykey,
                                 'cert': mycert
                             })
    else:
        server = MicroWebSrv(webPath='www/')
    server.MaxWebSocketRecvLen = 256
    server.WebSocketThreaded = False
    server.AcceptWebSocketCallback = acceptWebSocketCallback
    server.Start(threaded=True)
    return server
コード例 #4
0
    def __init__(self,
                 things,
                 port=80,
                 hostname=None,
                 ssl_options=None,
                 additional_routes=None):
        """
        Initialize the WebThingServer.

        For documentation on the additional route format, see:
        https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki/microWebSrv

        things -- list of Things managed by this server
        port -- port to listen on (defaults to 80)
        hostname -- Optional host name, i.e. mything.com
        ssl_options -- dict of SSL options to pass to the tornado server
        additional_routes -- list of additional routes to add to the server
        """
        self.ssl_suffix = '' if ssl_options is None else 's'

        self.things = things
        self.name = things.get_name()
        self.port = port
        self.hostname = hostname

        station = network.WLAN(dhcp_hostname="myhost")
        mac = station.config('mac')
        self.system_hostname = 'esp32-upy-{:02x}{:02x}{:02x}'.format(
            mac[3], mac[4], mac[5])

        self.hosts = [
            'localhost',
            'localhost:{}'.format(self.port),
            '{}.local'.format(self.system_hostname),
            '{}.local:{}'.format(self.system_hostname, self.port),
        ]

        for address in get_addresses():
            self.hosts.extend([
                address,
                '{}:{}'.format(address, self.port),
            ])

        if self.hostname is not None:
            self.hostname = self.hostname.lower()
            self.hosts.extend([
                self.hostname,
                '{}:{}'.format(self.hostname, self.port),
            ])

        if isinstance(self.things, MultipleThings):
            log.info('Registering multiple things')
            for idx, thing in enumerate(self.things.get_things()):
                thing.set_href_prefix('/{}'.format(idx))

            handlers = [
                ('/.*', 'OPTIONS', self.optionsHandler),
                ('/', 'GET', self.thingsGetHandler),
                ('/<thing_id>', 'GET', self.thingGetHandler),
                ('/<thing_id>/properties', 'GET', self.propertiesGetHandler),
                ('/<thing_id>/properties/<property_name>', 'GET',
                 self.propertyGetHandler),
                ('/<thing_id>/properties/<property_name>', 'PUT',
                 self.propertyPutHandler),
            ]
        else:
            log.info('Registering a single thing')
            handlers = [
                ('/.*', 'OPTIONS', self.optionsHandler),
                ('/', 'GET', self.thingGetHandler),
                ('/properties', 'GET', self.propertiesGetHandler),
                ('/properties/<property_name>', 'GET',
                 self.propertyGetHandler),
                ('/properties/<property_name>', 'PUT',
                 self.propertyPutHandler),
            ]

        if isinstance(additional_routes, list):
            handlers = additional_routes + handlers

        self.server = MicroWebSrv(webPath='/flash/www',
                                  routeHandlers=handlers,
                                  port=port)
        self.server.MaxWebSocketRecvLen = 256
        self.WebSocketThreaded = ws_run_in_thread
        self.server.WebSocketStackSize = 8 * 1024
        self.server.AcceptWebSocketCallback = self._acceptWebSocketCallback
コード例 #5
0
class WebThingServer:
    """Server to represent a Web Thing over HTTP."""
    def __init__(self,
                 things,
                 port=80,
                 hostname=None,
                 ssl_options=None,
                 additional_routes=None):
        """
        Initialize the WebThingServer.

        For documentation on the additional route format, see:
        https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki/microWebSrv

        things -- list of Things managed by this server
        port -- port to listen on (defaults to 80)
        hostname -- Optional host name, i.e. mything.com
        ssl_options -- dict of SSL options to pass to the tornado server
        additional_routes -- list of additional routes to add to the server
        """
        self.ssl_suffix = '' if ssl_options is None else 's'

        self.things = things
        self.name = things.get_name()
        self.port = port
        self.hostname = hostname

        station = network.WLAN(dhcp_hostname="myhost")
        mac = station.config('mac')
        self.system_hostname = 'esp32-upy-{:02x}{:02x}{:02x}'.format(
            mac[3], mac[4], mac[5])

        self.hosts = [
            'localhost',
            'localhost:{}'.format(self.port),
            '{}.local'.format(self.system_hostname),
            '{}.local:{}'.format(self.system_hostname, self.port),
        ]

        for address in get_addresses():
            self.hosts.extend([
                address,
                '{}:{}'.format(address, self.port),
            ])

        if self.hostname is not None:
            self.hostname = self.hostname.lower()
            self.hosts.extend([
                self.hostname,
                '{}:{}'.format(self.hostname, self.port),
            ])

        if isinstance(self.things, MultipleThings):
            log.info('Registering multiple things')
            for idx, thing in enumerate(self.things.get_things()):
                thing.set_href_prefix('/{}'.format(idx))

            handlers = [
                ('/.*', 'OPTIONS', self.optionsHandler),
                ('/', 'GET', self.thingsGetHandler),
                ('/<thing_id>', 'GET', self.thingGetHandler),
                ('/<thing_id>/properties', 'GET', self.propertiesGetHandler),
                ('/<thing_id>/properties/<property_name>', 'GET',
                 self.propertyGetHandler),
                ('/<thing_id>/properties/<property_name>', 'PUT',
                 self.propertyPutHandler),
            ]
        else:
            log.info('Registering a single thing')
            handlers = [
                ('/.*', 'OPTIONS', self.optionsHandler),
                ('/', 'GET', self.thingGetHandler),
                ('/properties', 'GET', self.propertiesGetHandler),
                ('/properties/<property_name>', 'GET',
                 self.propertyGetHandler),
                ('/properties/<property_name>', 'PUT',
                 self.propertyPutHandler),
            ]

        if isinstance(additional_routes, list):
            handlers = additional_routes + handlers

        self.server = MicroWebSrv(webPath='/flash/www',
                                  routeHandlers=handlers,
                                  port=port)
        self.server.MaxWebSocketRecvLen = 256
        self.WebSocketThreaded = ws_run_in_thread
        self.server.WebSocketStackSize = 8 * 1024
        self.server.AcceptWebSocketCallback = self._acceptWebSocketCallback

    def start(self):
        """Start listening for incoming connections."""
        # If WebSocketS used and NOT running in thread, and WebServer IS
        # running in thread make shure WebServer has enough stack size to
        # handle also the WebSocket requests.
        log.info('Starting Web Server on port {}'.format(self.port))
        self.server.Start(threaded=srv_run_in_thread)

        mdns = network.mDNS()
        mdns.start(self.system_hostname, 'MicroPython with mDNS')
        mdns.addService('_webthing', '_tcp', 80, self.system_hostname, {
            'board': 'ESP32',
            'path': '/',
        })

    def stop(self):
        """Stop listening."""
        self.server.Stop()

    def getThing(self, routeArgs):
        """Get the thing ID based on the route."""
        if not routeArgs or 'thing_id' not in routeArgs:
            thing_id = None
        else:
            thing_id = routeArgs['thing_id']

        return self.things.get_thing(thing_id)

    def getProperty(self, routeArgs):
        """Get the property name based on the route."""
        thing = self.getThing(routeArgs)
        if thing:
            property_name = routeArgs['property_name']
            if thing.has_property(property_name):
                return thing, thing.find_property(property_name)
        return None, None

    def getHeader(self, headers, key, default=None):
        standardized = {k.lower(): v for k, v in headers.items()}
        return standardized.get(key, default)

    def validateHost(self, headers):
        """Validate the Host header in the request."""
        host = self.getHeader(headers, 'host')
        if host is not None and host.lower() in self.hosts:
            return True

        return False

    @print_exc
    def optionsHandler(self, httpClient, httpResponse, routeArgs=None):
        """Handle an OPTIONS request to any path."""
        if not self.validateHost(httpClient.GetRequestHeaders()):
            httpResponse.WriteResponseError(403)
            return

        httpResponse.WriteResponse(204, _CORS_HEADERS, None, None, None)

    @print_exc
    def thingsGetHandler(self, httpClient, httpResponse):
        """Handle a request to / when the server manages multiple things."""
        if not self.validateHost(httpClient.GetRequestHeaders()):
            httpResponse.WriteResponseError(403)
            return

        headers = httpClient.GetRequestHeaders()
        base_href = 'http{}://{}'.format(self.ssl_suffix,
                                         self.getHeader(headers, 'host', ''))
        ws_href = 'ws{}://{}'.format(self.ssl_suffix,
                                     self.getHeader(headers, 'host', ''))

        descriptions = []
        for thing in self.things.get_things():
            description = thing.as_thing_description()
            description['links'].append({
                'rel':
                'alternate',
                'href':
                '{}{}'.format(ws_href, thing.get_href()),
            })
            description['href'] = thing.get_href()
            description['base'] = '{}{}'.format(base_href, thing.get_href())
            description['securityDefinitions'] = {
                'nosec_sc': {
                    'scheme': 'nosec',
                },
            }
            description['security'] = 'nosec_sc'
            descriptions.append(description)

        httpResponse.WriteResponseJSONOk(
            obj=descriptions,
            headers=_CORS_HEADERS,
        )

    @print_exc
    def thingGetHandler(self, httpClient, httpResponse, routeArgs=None):
        """Handle a GET request for an individual thing."""
        if not self.validateHost(httpClient.GetRequestHeaders()):
            httpResponse.WriteResponseError(403)
            return

        thing = self.getThing(routeArgs)
        if thing is None:
            httpResponse.WriteResponseNotFound()
            return

        headers = httpClient.GetRequestHeaders()
        base_href = 'http{}://{}'.format(self.ssl_suffix,
                                         self.getHeader(headers, 'host', ''))
        ws_href = 'ws{}://{}'.format(self.ssl_suffix,
                                     self.getHeader(headers, 'host', ''))

        description = thing.as_thing_description()
        description['links'].append({
            'rel':
            'alternate',
            'href':
            '{}{}'.format(ws_href, thing.get_href()),
        })
        description['base'] = '{}{}'.format(base_href, thing.get_href())
        description['securityDefinitions'] = {
            'nosec_sc': {
                'scheme': 'nosec',
            },
        }
        description['security'] = 'nosec_sc'

        httpResponse.WriteResponseJSONOk(
            obj=description,
            headers=_CORS_HEADERS,
        )

    @print_exc
    def propertiesGetHandler(self, httpClient, httpResponse, routeArgs=None):
        """Handle a GET request for a property."""
        thing = self.getThing(routeArgs)
        if thing is None:
            httpResponse.WriteResponseNotFound()
            return
        httpResponse.WriteResponseJSONOk(thing.get_properties())

    @print_exc
    def propertyGetHandler(self, httpClient, httpResponse, routeArgs=None):
        """Handle a GET request for a property."""
        if not self.validateHost(httpClient.GetRequestHeaders()):
            httpResponse.WriteResponseError(403)
            return

        thing, prop = self.getProperty(routeArgs)
        if thing is None or prop is None:
            httpResponse.WriteResponseNotFound()
            return
        httpResponse.WriteResponseJSONOk(
            obj={prop.get_name(): prop.get_value()},
            headers=_CORS_HEADERS,
        )

    @print_exc
    def propertyPutHandler(self, httpClient, httpResponse, routeArgs=None):
        """Handle a PUT request for a property."""
        if not self.validateHost(httpClient.GetRequestHeaders()):
            httpResponse.WriteResponseError(403)
            return

        thing, prop = self.getProperty(routeArgs)
        if thing is None or prop is None:
            httpResponse.WriteResponseNotFound()
            return
        args = httpClient.ReadRequestContentAsJSON()
        if args is None:
            httpResponse.WriteResponseBadRequest()
            return
        try:
            prop.set_value(args[prop.get_name()])
        except PropertyError:
            httpResponse.WriteResponseBadRequest()
            return
        httpResponse.WriteResponseJSONOk(
            obj={prop.get_name(): prop.get_value()},
            headers=_CORS_HEADERS,
        )

    # === MicroWebSocket callbacks ===

    @print_exc
    def _acceptWebSocketCallback(self, webSocket, httpClient):
        reqPath = httpClient.GetRequestPath()
        if WS_messages:
            print('WS ACCEPT reqPath =', reqPath)
            if ws_run_in_thread or srv_run_in_thread:
                # Print thread list so that we can monitor maximum stack size
                # of WebServer thread and WebSocket thread if any is used
                _thread.list()
        webSocket.RecvTextCallback = self._recvTextCallback
        webSocket.RecvBinaryCallback = self._recvBinaryCallback
        webSocket.ClosedCallback = self._closedCallback
        things = self.things.get_things()
        if len(things) == 1:
            thing_id = 0
        else:
            thing_id = int(reqPath.split('/')[1])
        thing = things[thing_id]
        webSocket.thing = thing
        thing.add_subscriber(webSocket)

    @print_exc
    def _recvTextCallback(self, webSocket, msg):
        if WS_messages:
            print('WS RECV TEXT : %s' % msg)

    @print_exc
    def _recvBinaryCallback(self, webSocket, data):
        if WS_messages:
            print('WS RECV DATA : %s' % data)

    @print_exc
    def _closedCallback(self, webSocket):
        if WS_messages:
            if ws_run_in_thread or srv_run_in_thread:
                _thread.list()
            print('WS CLOSED')