#!/usr/bin/python3 """Hello-world using aiocoap. CoAP is a protocol designed by a man named C.Bor as a simpler, more efficient HTTP to a point where it fits comfortably on an 8-bit microcontroller such as an AVR, so that distributed applications for networks of microcontrollers can be designed within the constraints of the REST architectural style, receiving the same benefits. Also, it works as a way to expose microcontroller functionality to larger processors and vice versa. RFC 7252 has more information. There is an implementation of CoAP called `aiocoap` for Python 3’s asyncio library, derived from a Twisted implementation called txThings. This is an example program for it, derived from the examples in the `aiocoap` documentation. Rather surprisingly, despite CoAP’s design emphasis on efficiency, this server-client pair is only able to achieve about 200 requests per second. httpdito on the same (quad-core) hardware gets a bit over 5100 requests per second (for a 15-byte file), and ZeroMQ on the same hardware can push and pull 2.5 million messages per second. """ import argparse import asyncio import logging import sys import textwrap import aiocoap import aiocoap.resource class LoggingResource(aiocoap.resource.Resource): def __init__(self, logger): super().__init__() self.logger = logger @asyncio.coroutine def render_get(self, request): self.logger.info("request from %r", request.remote) return aiocoap.Message(code=aiocoap.CONTENT, payload=b"how old is your cat") class ErrorResource(aiocoap.resource.Resource): @asyncio.coroutine def render_get(self, request): raise aiocoap.error.RenderableError("fat") class UnparsableErrorResource(aiocoap.resource.Resource): @asyncio.coroutine def render_get(self, request): raise aiocoap.error.UnparsableMessage() class ValueErrorResource(aiocoap.resource.Resource): @asyncio.coroutine def render_get(self, request): raise ValueError('$') def server(): s = aiocoap.resource.Site() logger = logging.getLogger(__name__) s.add_resource(('hi',), LoggingResource(logger)) s.add_resource(('err',), ErrorResource()) s.add_resource(('doh',), UnparsableErrorResource()) s.add_resource(('val',), ValueErrorResource()) # ensure_future() used to be called async() future = asyncio.ensure_future(aiocoap.Context.create_server_context(s)) loop = asyncio.get_event_loop() context = loop.run_until_complete(future) logger.info("running CoAP server on %r", context.transport._extra['socket'].getsockname()) loop.run_forever() @asyncio.coroutine def client(uri, n, verbosity): context = yield from aiocoap.Context.create_client_context() for i in range(n): req = aiocoap.Message(code=aiocoap.GET) req.set_request_uri(uri) response = yield from context.request(req).response if verbosity or i == n-1: print("Code", response.code) print("Payload", response.payload) print("Remote", response.remote) def main(): parser = argparse.ArgumentParser(description="CoAP hello-world.", epilog=textwrap.dedent(""" The server side of this program doesn’t do anything useful, but it does demonstrate how to get the remote client address. """)) subparsers = parser.add_subparsers(dest='subparser') server_help = 'Run a CoAP server with resource /hi' server_parser = subparsers.add_parser('server', help=server_help) client_help = 'GET a given CoAP URL' client_parser = subparsers.add_parser('client', help=client_help) client_parser.add_argument('url', nargs='?', default="coap://localhost/hi", help="default 'coap://localhost/hi'") client_parser.add_argument('-n', type=int, default=1, help='The number of times to fetch the URL') client_parser.add_argument('-v', action='count', help='Print all responses, not just the last') args = parser.parse_args() if args.subparser == 'server': logging.basicConfig(level=logging.INFO) return server() elif args.subparser == 'client': future = client(args.url, args.n, args.v) return asyncio.get_event_loop().run_until_complete(future) else: parser.print_help() return -1 if __name__ == '__main__': sys.exit(main())