aiocoap – The Python CoAP library¶
The aiocoap package is a Python implementation of CoAP, the Constrained Application Protocol (RFC 7252, more info at http://coap.technology/).
It uses the Python 3’s asynchronous I/O to facilitate concurrent operations while maintaining a simple to use interface and not depending on anything outside the standard library.
aiocoap is originally based on txThings. If you want to use CoAP in your existing twisted application, or can not migrate to Python 3 yet, that is probably more useful to you than aiocoap.
Usage¶
For how to use the aiocoap library, have a look at the Guided Tour through aiocoap, or at
the Usage Examples and CoAP tools provided. All the details are in the
aiocoap module
documentation.
All examples can be run directly from a source code copy. If you prefer to install it, the usual Python mechanisms apply (see Installing aiocoap).
Features / Standards¶
This library supports the following standards in full or partially:
- RFC7252 (CoAP): missing are a caching and cross proxy implementation, proper multicast (support is incomplete), and DTLS.
- RFC7641 (Observe): Reordering, re-registration, and active cancellation are missing.
- RFC7959 (Blockwise): Multicast exceptions missing.
- draft-ietf-core-etch-04: Only registry entries added, but that should be all that’s neede on the library side.
If something described by one of the standards but not implemented, it is considered a bug; please file at the github issue tracker. (If it’s not on the list or in the excluded items, file a wishlist item at the same location).
Dependencies¶
The core aiocoap functionality works out of the box with Python 3.4; with the additional asyncio module, it works with version 3.3 as well. The Usage Examples require Python 3.5 or newer.
When application/link-format typed resources (RFC 6690) are supposed to be
used, the link_header module is required as well. When the respective code
paths are used without the module, an ImportError
will be raised, or a
5.00 status code will be returned. Python modules that implement servers or
use discovery with aiocoap should declare a dependency on
'aiocoap[linkheader]'
instead of 'aiocoap'
.
Development¶
aiocoap tries to stay close to PEP8 recommendations and general best practice,
and should thus be easy to contribute to. Unit tests are implemented in the
./tests/
directory and easiest run using ./setup.py test
; complete test
coverage is aimed for, but not yet complete (and might never be, as the error
handling for pathological network partners is hard to trigger with a library
designed not to misbehave).
Documentation is built using sphinx with ./setup.py build_sphinx
; hacks
used there are described in ./doc/README.doc
.
Bugs (ranging from “design goal” and “wishlist” to typos) are currently tracked in the github issue tracker.
Relevant URLs¶
https://github.com/chrysn/aiocoap
This is where the latest source code can be found, and bugs can be reported. Generally, this serves as the project web site.
http://aiocoap.readthedocs.org/
Online documentation built from the sources.
-
Further general information on CoAP, the standard documents involved, and other implementations and tools available.
Licensing¶
aiocoap is published under the MIT License, see LICENSE for details.
When using aiocoap for a publication, please cite it according to the output of
./setup.py cite [--bibtex]
.
- Copyright (c) 2012-2014 Maciej Wasilak <http://sixpinetrees.blogspot.com/>,
- 2013-2014 Christian Amsüss <c.amsuess@energyharvesting.at>
Installing aiocoap¶
In most situations, it is recommended to install the latest released version of aiocoap. If you do not use a distribution that has aiocoap packaged, or if you use Python’s virtual environments, this is done with
$ pip install --upgrade "aiocoap[linkheader]"
The [linkheader]
makes sure that even optional dependencies are installed.
If pip
is not available on your platform, you can manually download and
unpack the latest .tar.gz
file from the Python package index and run
$ ./setup.py install
Development version¶
If you want to play with aiocoap’s internals or consider contributing to the project, the suggested way of operation is getting a Git checkout of the project:
$ git clone https://github.com/chrysn/aiocoap
You can then use the project from that location, or install it with
$ pip install --upgrade ".[linkheader]"
If you need to install the latest development version of aiocoap but do not plan on editing (eg. because you were asked in the course of a bug report to test something against the latest aiocoap version), you can install it directly from the web:
$ pip install --upgrade "git+https://github.com/chrysn/aiocoap#egg=aiocoap[linkheader]"
With the -e
option, that is also a viable option if you want to modify
aiocoap and pip’s choice of checkout directories is suitable for you.
Guided Tour through aiocoap¶
This page gets you started on the concepts used in aiocoap; it will assume rough familiarity with what CoAP is, and a working knowledge of Python development, but introduce you to asynchronous programming and explain some CoAP concepts along with the aiocoap API.
First, some tools¶
Before we get into programming, let’s establish tools with which we can probe a server, and a server itself.
Start off with the sample server by running the following in a terminal:
$ ./server.py
Note
The $
sign indicates the prompt; you enter everything after it in
a terminal shell. Lines not starting with a dollar sign are the program
output, if any. Later on, we’ll see lines starting with >>>
; those are
run inside a Python interpreter.
I recommend that you use the IPython interpreter. One useful feature for
following through this tutorial is that you can copy full lines (including
any >>>
parts) to the clipboard and use the %paste
IPython command
to run it, taking care of indentation etc.
This has started a CoAP server with some demo content, and keeps running until you terminate it with Ctrl-C.
In a separate terminal, use the aiocoap-client tool to send a GET request to the server:
$ ./aiocoap-client coap://localhost/.well-known/core
</time>; obs, </.well-known/core>; ct=40, </other/separate>, </other/block>
The address we’re using here is a resource on the local machine (localhost
)
at the well-known location .well-known/core
, which in CoAP is the go-to
location if you don’t know anything about the paths on the server beforehand.
It tells that there is a resource at the path /time
that has the obs
ervable attribute, a resource at the path /.well-known/core
, and two more
at /other/separate
and /other/block
.
Note
Getting “5.00 Internal Server Error” instead? Install the link_header module and restart the server, or trust me that the output would look like that if it were installed and proceed.
Note
There can be a “(No newline at end of message)” line below your output. This just makes sure your prompt does not start in the middle of the screen. I’ll just ignore that.
Let’s see what /time
gives us:
$ ./aiocoap-client coap://localhost/time
2016-12-07 10:08
The response should have arrived immediately: The client sent a message to the
server in which it requested the resource at /time
, and the server could
right away send a message back. In contrast, /other/separate
is slower:
$ ./aiocoap-client coap://localhost/others/separate
Three rings for the elven kings [abbreviated]
The response to this message comes back with a delay. Here, it is simulated by the server; in real-life situations, this delay can stem from network latency, servers waiting for some sensor to read out a value, slow hard drives etc.
A request¶
In order to run a similar request programmatically, we’ll need a request message:
>>> from aiocoap import *
>>> msg = Message(code=GET, uri="coap://localhost/other/separate")
>>> print(msg)
<aiocoap.Message at 0x0123deadbeef: None GET (ID None, token b'') remote None, 2 option(s)>
The message consists of several parts. The non-optional ones are largely handled by aiocoap (message type, ID, token and remote are all None or empty here and will be populated when the message is sent). The options are roughly equivalent to what you might know as HTTP headers:
>>> msg.opt
<aiocoap.options.Options at 0x0123deadbef0: URI_HOST: localhost, URI_PATH: other / separate>
You might have noticed that the Uri-Path option has whitespace around the slash. This is because paths in CoAP are not a structured byte string with slashes in it (as they are in HTTP), but actually repeated options of a (UTF-8) string, which are represented as a tuple in Python:
>>> msg.opt.uri_path
('other', 'separate')
Now to send that network as a request over the network, we’ll need a network protocol object. That has a request method, and can give a response (bear with me, these examples don’t actually work):
>>> protocol.request(msg).response
<Future pending cb=[Request._response_cancellation_handler()]>
That is obviously not a proper response – yet. If the protocol returned a finished response, the program couldn’t do any work in the meantime. Because a Future is returned, the user can start other requests in parallel, or do other processing in the meantime. For now, all we want is to wait until the response is ready:
>>> await protocol.request(msg).response
<aiocoap.Message at 0x0123deadbef1: Type.CON 2.05 Content (ID 51187, token b'\x00\x00\x81\x99') remote <UDP6EndpointAddress [::ffff:127.0.0.1]:5683 with local address>, 186 byte(s) payload>
Here, we have a successful message (“2.05 Content” is the rough equivalent of HTTP’s “200 OK”, and the 186 bytes of payload look promising). Until we can dissect that, we’ll have to get those asynchronous things to work properly, though.
Asynchronous operation¶
The interactive Python shell does not work in an asynchronous fashion (yet?) – it follows a strict “read, evaluate, print” loop (REPL), similar to how a Python program as a whole is executed. To launch asynchronous processing, we’ll use the following shorthand:
>>> import asyncio
>>> run = asyncio.get_event_loop().run_until_complete
With that, we can run asynchronous functions; note that any function that
await
s anything is itself asynchronous and has to be declared
accordingly. Now we can run what did not work before:
>>> async def main():
... protocol = await Context.create_client_context()
... msg = Message(code=GET, uri="coap://localhost/other/separate")
... response = await protocol.request(msg).response
... print(response)
>>> run(main())
<aiocoap.Message at 0x0123deadbef1: Type.CON 2.05 Content (ID 51187, token b'\x00\x00\x81\x99') remote <UDP6EndpointAddress [::ffff:127.0.0.1]:5683 with local address>, 186 byte(s) payload>
That’s better!
(Now the protocol
object could also be created. That doesn’t actually take
long time, but could, depending on the operating system).
The response¶
To dissect the response, let’s make sure we have it available:
>>> protocol = run(Context.create_client_context())
>>> msg = Message(code=GET, uri="coap://localhost/other/separate")
>>> response = run(protocol.request(msg).response)
>>> print(response)
<aiocoap.Message at 0x0123deadbef1: Type.CON 2.05 Content (ID 51187, token b'\x00\x00\x81\x99') remote <UDP6EndpointAddress [::ffff:127.0.0.1]:5683 with local address>, 186 byte(s) payload>
The response obtained in the main function is a message like the request message, just that it has a different code (2.05 is of the successful 2.00 group), incidentally no options (because it’s a very simple server), and actual data.
The response code is represented in Python by an enum with some utility functions; the remote address (actually remote-local address pair) is an object too:
>>> response.code
<Successful Response Code 69 "2.05 Content">
>>> response.code.is_successful()
True
>>> response.remote.hostinfo
'[::ffff:127.0.0.1]'
>>> response.remote.is_multicast
False
The actual response message, the body, or the payload of the response, is accessible in the payload property, and is always a bytestring:
>>> response.payload
b'Three rings for the elven kings [ abbreviated ]'
aiocoap does not yet provide utilities to parse the message according to its
content format (which would be accessed as response.opt.content_format
and
is numeric in CoAP).
More asynchronous fun
The other examples don’t show simultaneous requests in flight, so let’s have one with parallel requests:
>>> async def main(): ... responses = [ ... protocol.request(Message(code=GET, uri=u)).response ... for u ... in ("coap://localhost/time", "coap://vs0.inf.ethz.ch/obs", "coap://coap.me/test") ... ] ... for f in asyncio.as_completed(responses): ... response = await f ... print("Response from {}: {}".format(response.get_request_uri(), response.payload)) >>> run(main()) Response from coap://localhost/time: b'2016-12-07 18:16' Response from coap://vs0.inf.ethz.ch/obs: b'18:16:11' Response from coap://coap.me/test: b'welcome to the ETSI plugtest! last change: 2016-12-06 16:02:33 UTC'
This also shows that the response messages do keep some information of their original request (in particular, the request URI) with them to ease further parsing.
This is currently the end of the guided tour; see the aiocoap.resource
documentation for the server side until the tour covers that too.is complete.
aiocoap module¶
aiocoap¶
The aiocoap package is a library that implements CoAP, the Constrained Application Protocol (RFC 7252, more info at http://coap.technology/).
Usage¶
In all but the most exotic applications, you will want to create a single
Context
instance that binds to the network. The
Context.create_client_context()
and
Context.create_server_context()
coroutines give you a readily connected
context.
On the client side, you can request resources by assembling a Message
and passing it to your context’s Context.request()
method, which
returns a protocol.Request
object with a
protocol.Request.response
future (which is a Message
again).
On the server side, a resource tree gets built from
aiocoap.resource.Resource
objects into a
aiocoap.resource.Site
, which is assigned to the context at creation
time.
aiocoap.protocol module¶
This module contains the classes that are responsible for keeping track of messages:
Context
roughly represents the CoAP endpoint (basically a UDP socket) – something that can send requests and possibly can answer incoming requests.- a
Request
gets generated whenever a request gets sent to keep track of the response - a
Responder
keeps track of a single incoming request
-
class
aiocoap.protocol.
Context
(loop=None, serversite=None, loggername='coap')¶ Bases:
asyncio.protocols.DatagramProtocol
,aiocoap.interfaces.RequestProvider
An object that passes messages between an application and the network
A
Context
gets bound to a network interface as an asyncio protocol. It manages the basic CoAP network mechanisms like message deduplication and retransmissions, and delegates management of blockwise transfer as well as the details of matching requests with responses to theRequest
andResponder
classes.In that respect, a Context (as currently implemented) is also an endpoint. It is anticipated, though, that issues arise due to which the implementation won’t get away with creating a single socket, and that it will be required to deal with multiple endpoints. (E.g. the V6ONLY=0 option is not portable to some OS, and implementations might need to bind to different ports on different interfaces in multicast contexts). When those distinctions will be implemented, message dispatch will stay with the context, which will then deal with the individual endpoints.
In a way, a
Context
is the single object all CoAP messages that get treated by a single application pass by.Instead of passing a protocol factory to the asyncio loop’s create_datagram_endpoint method, the following convenience functions are recommended for creating a context:
-
classmethod
create_client_context
(*, dump_to=None, loggername='coap', loop=None)¶ Create a context bound to all addresses on a random listening port.
This is the easiest way to get an context suitable for sending client requests.
-
classmethod
create_server_context
(site, bind=('::', 5683), *, dump_to=None, loggername='coap-server', loop=None)¶ Create an context, bound to all addresses on the CoAP port (unless otherwise specified in the
bind
argument).This is the easiest way to get a context suitable both for sending client and accepting server requests.
If you choose to create the context manually, make sure to wait for its
ready
future to complete, as only then can messages be sent.A context’s public API consists of the
send_message()
function, theoutgoing_requests
,incoming_requests
andoutgoing_obvservations
dictionaries, and theserversite
object, but those are not stabilized yet, and for most applications the following convenience functions are more suitable:-
request
(request, **kwargs)¶ TODO: create a proper interface to implement and deprecate direct instanciation again
-
multicast_request
(request)¶
If more control is needed, eg. with observations, create a
Request
yourself and pass the context to it.-
outgoing_requests
= None¶ Unfinished outgoing requests (identified by token and remote)
-
incoming_requests
= None¶ Unfinished incoming requests.
(path-tuple, remote): Request
-
outgoing_observations
= None¶ Observations where this context acts as client.
(token, remote) -> weak(ClientObservation)
-
incoming_observations
= None¶ Observation where this context acts as server.
(token, remote) -> ServerObservation
. This is managed by :cls:ServerObservation andResponder.handle_observe_request()
.
-
shutdown
()¶ Take down the listening socket and stop all related timers.
After this coroutine terminates, and once all external references to the object are dropped, it should be garbage-collectable.
-
fill_remote
(message)¶
-
send_message
(message, exchange_monitor=None)¶ Encode and send message. This takes care of retransmissions (if CON), message IDs and rate limiting, but does not hook any events to responses. (Use the
Request
class or responding resources instead; those are the typical callers of this function.)If notification about the progress of the exchange is required, an ExchangeMonitor can be passed in, which will receive the appropriate callbacks.
-
next_token
()¶ Reserve and return a new Token for request.
-
request
(request, **kwargs) TODO: create a proper interface to implement and deprecate direct instanciation again
-
multicast_request
(request)
-
classmethod
create_client_context
(*, dump_to=None, loggername='coap', loop=None) Create a context bound to all addresses on a random listening port.
This is the easiest way to get an context suitable for sending client requests.
-
classmethod
create_server_context
(site, bind=('::', 5683), *, dump_to=None, loggername='coap-server', loop=None) Create an context, bound to all addresses on the CoAP port (unless otherwise specified in the
bind
argument).This is the easiest way to get a context suitable both for sending client and accepting server requests.
-
kill_transactions
(remote, exception=<class 'aiocoap.error.CommunicationKilled'>)¶ Abort all pending exchanges and observations to a given remote.
The exact semantics of this are not yet completely frozen – currently, pending exchanges are treated as if they timeouted, server sides of observations are droppedn and client sides of observations receive an errback.
Requests that are not part of an exchange, eg. NON requests or requests that are waiting for their responses after an empty ACK are currently not handled.
-
classmethod
-
class
aiocoap.protocol.
BaseRequest
¶ Bases:
object
Common mechanisms of
Request
andMulticastRequest
-
class
aiocoap.protocol.
Request
(protocol, app_request, exchange_monitor_factory=<function Request.<lambda>>, handle_blockwise=True)¶ Bases:
aiocoap.protocol.BaseRequest
,aiocoap.interfaces.Request
Class used to handle single outgoing request.
Class includes methods that handle sending outgoing blockwise requests and receiving incoming blockwise responses.
-
cancel
()¶
-
send_request
(request)¶ Send a request or single request block.
This method is used in 3 situations: - sending non-blockwise request - sending blockwise (Block1) request block - asking server to send blockwise (Block2) response block
-
handle_response
(response)¶
-
process_block1_in_response
(response)¶ Process incoming response with regard to Block1 option.
-
process_block2_in_response
(response)¶ Process incoming response with regard to Block2 option.
-
handle_final_response
(response)¶
-
register_observation
(response)¶
-
response_raising
¶ An awaitable that returns if a response comes in and is successful, otherwise raises generic network exception or a
error.ResponseWrappingError
for unsuccessful responses.Experimental Interface.
-
response_nonraising
¶ An awaitable that rather returns a 500ish fabricated message (as a proxy would return) instead of raising an exception.
Experimental Interface.
-
-
class
aiocoap.protocol.
MulticastRequest
(protocol, request)¶ Bases:
aiocoap.protocol.BaseRequest
-
handle_response
(response)¶
-
-
class
aiocoap.protocol.
Responder
(protocol, request, exchange_monitor_factory=<function Responder.<lambda>>)¶ Bases:
object
Handler for an incoming request or (in blockwise) a group thereof
Class includes methods that handle receiving incoming blockwise requests (only atomic operation on complete requests), searching for target resources, preparing responses and sending outgoing blockwise responses.
To keep an eye on exchanges going on, a factory for ExchangeMonitor can be passed in that generates a monitor for every single message exchange created during the response.
-
handle_next_request
(request)¶
-
process_block1_in_request
(request)¶ Process an incoming request while in block1 phase.
This method is responsible for finishing the app_request future and thus indicating that it should not be called any more, or scheduling itself again.
-
dispatch_request
(initial_block)¶ Dispatch incoming request - search context resource tree for resource in Uri Path and call proper CoAP Method on it.
-
respond_with_error
(request, code, payload)¶ Helper method to send error response to client.
-
respond
(app_response, request)¶ Take application-supplied response and prepare it for sending.
-
process_block2_in_request
(request)¶ Process incoming request with regard to Block2 option
Method is recursive - calls itself until all response blocks are sent to client.
-
send_non_final_response
(response, request)¶ Helper method to send a response to client, and setup a timeout for client. This also registers the responder with the protocol again to receive the next message.
-
send_final_response
(response, request)¶
-
send_response
(response, request)¶ Send a response or single response block.
This method is used in 4 situations: - sending success non-blockwise response - asking client to send blockwise (Block1) request block - sending blockwise (Block2) response block - sending any error response
-
send_empty_ack
(request)¶ Send separate empty ACK when response preparation takes too long.
Currently, this can happen only once per Responder, that is, when the last block1 has been transferred and the first block2 is not ready yet.
-
handle_observe_request
(request)¶
-
handle_observe_response
(request, response)¶ Modify the response according to the Responder’s understanding of the involved observation (eg. drop the observe flag it’s not involved in an observation or the observation was cancelled), and update the Responder/context if the response modifies the observation state (eg. by being unsuccessful).
-
-
class
aiocoap.protocol.
ExchangeMonitor
¶ Bases:
object
Callback collection interface to keep track of what happens to an exchange.
Callbacks will be called in sequence:
enqueued{0,1} sent retransmitted{0,MAX_RETRANSMIT} (timeout | rst | cancelled | response)
; everything aftersent
only gets called if the messae that initiated the exchange was a CON.-
enqueued
()¶
-
sent
()¶
-
retransmitted
()¶
-
timeout
()¶
-
rst
()¶
-
cancelled
()¶
-
response
(message)¶
-
-
class
aiocoap.protocol.
ServerObservation
(original_protocol, original_request, requester_log)¶ Bases:
object
An active CoAP observation inside a server is described as a ServerObservation object.
It keeps a complete copy of the original request for simplicity (while it actually would only need parts of that request, like the accept option).
A ServerObservation has two boolean states: accepted and cancelled. It is originally neither, gets accepted when a
ObservableResource.add_observation()
method doesaccept()
it, and gets cancelled by incoming packages of the same identifier, RST/timeout on notifications or the observed resource. Beware that an accept can happen after cancellation if the client changes his mind quickly, but the resource takes time to decide whether it can be observed.-
accept
(cancellation_callback)¶
-
deregister
(reason)¶
-
identifier
¶
-
static
request_key
(request)¶
-
trigger
(response=None)¶
-
class
ObservationExchangeMonitor
(observation)¶ Bases:
aiocoap.protocol.ExchangeMonitor
These objects feed information about the success or failure of a response back to the observation.
Note that no information flows to the exchange monitor from the observation, so they may outlive the observation and need to check if it’s not already cancelled before cancelling it.
-
enqueued
()¶
-
sent
()¶
-
rst
()¶
-
timeout
()¶
-
-
-
class
aiocoap.protocol.
ClientObservation
(original_request)¶ Bases:
object
-
register_callback
(callback)¶ Call the callback whenever a response to the message comes in, and pass the response to it.
-
register_errback
(callback)¶ Call the callback whenever something goes wrong with the observation, and pass an exception to the callback. After such a callback is called, no more callbacks will be issued.
-
callback
(response)¶ Notify all listeners of an incoming response
-
error
(exception)¶ Notify registered listeners that the observation went wrong. This can only be called once.
-
cancel
()¶ Cease to generate observation or error events. This will not generate an error by itself.
-
aiocoap.message module¶
-
class
aiocoap.message.
Message
(*, mtype=None, mid=None, code=None, payload=b'', token=b'', uri=None, **kwargs)¶ Bases:
object
CoAP Message with some handling metadata
This object’s attributes provide access to the fields in a CoAP message and can be directly manipulated.
- Some attributes are additional data that do not round-trip through serialization and deserialization. They are marked as “non-roundtrippable”.
- Some attributes that need to be filled for submission of the message can be left empty by most applications, and will be taken care of by the library. Those are marked as “managed”.
The attributes are:
payload
: The payload (body) of the message as bytes.mtype
: Message type (CON, ACK etc, seenumbers.types
). Managed unless set by the application.code
: The code (either request or response code), seenumbers.codes
.opt
: A container for the options, seeoptions.Options
.mid
: The message ID. Managed by theContext
.token
: The message’s token as bytes. Managed by theContext
.remote
: The socket address of the other side, managed by theprotocol.Request
by resolving the.opt.uri_host
orunresolved_remote
, or theResponder
by echoing the incoming request’s. (If you choose to set this explicitly set this, make sure not to set incomplete IPv6 address tuples, as they can be sent but don’t compare equally with the responses). Non-roundtrippable.requested_*: Managed by the
protocol.Request
a response results from, and filled with the request’s URL data. Non-roundtrippable.unresolved_remote:
host[:port]
(strictly speaking; hostinfo as in a URI) formatted string. If this attribute is set, it overrides.opt.uri_host
(and-_port
) when it comes to filling theremote
in an outgoing request.Use this when you want to send a request with a host name that would not normally resolve to the destination address. (Typically, this is used for proxying.)
prepath
,postpath
: Not sure, will probably go away when resources are overhauled. Non-roundtrippable.
Options can be given as further keyword arguments at message construction time. This feature is experimental, as future message parameters could collide with options.
-
copy
(**kwargs)¶ Create a copy of the Message. kwargs are treated like the named arguments in the constructor, and update the copy.
-
classmethod
decode
(rawdata, remote=None)¶ Create Message object from binary representation of message.
-
encode
()¶ Create binary representation of message from Message object.
-
get_cache_key
(ignore_options=())¶ Generate a hashable and comparable object (currently a tuple) from the message’s code and all option values that are part of the cache key and not in the optional list of ignore_options (which is the list of option numbers that are not technically NoCacheKey but handled by the application using this method).
>>> m1 = Message(code=GET) >>> m2 = Message(code=GET) >>> m1.opt.uri_path = ('s', '1') >>> m2.opt.uri_path = ('s', '1') >>> m1.opt.size1 = 10 # the only no-cache-key option in the base spec >>> m2.opt.size1 = 20 >>> m1.get_cache_key() == m2.get_cache_key() True >>> m2.opt.etag = b'000' >>> m1.get_cache_key() == m2.get_cache_key() False >>> ignore = [OptionNumber.ETAG] >>> m1.get_cache_key(ignore) == m2.get_cache_key(ignore) True
-
get_request_uri
()¶ The absolute URI this message belongs to.
For requests, this is composed from the options (falling back to the remote). For responses, this is stored by the Request object not only to preserve the request information (which could have been kept by the requesting application), but also because the Request can know about multicast responses (which would update the host component) and redirects (FIXME do they exist?).
-
set_request_uri
(uri, *, set_uri_host=True)¶ Parse a given URI into the uri_* fields of the options.
The remote does not get set automatically; instead, the remote data is stored in the uri_host and uri_port options. That is because name resolution is coupled with network specifics the protocol will know better by the time the message is sent. Whatever sends the message, be it the protocol itself, a proxy wrapper or an alternative transport, will know how to handle the information correctly.
When
set_uri_host=False
is passed, the host/port is stored in theunresolved_remote
message property instead of the uri_host option; as a result, the unresolved host name is not sent on the wire, which breaks virtual hosts but makes message sizes smaller.
aiocoap.options module¶
-
class
aiocoap.options.
Options
¶ Bases:
object
Represent CoAP Header Options.
-
decode
(rawdata)¶ Passed a CoAP message body after the token as rawdata, fill self with the options starting at the beginning of rawdata, an return the rest of the message (the body).
-
encode
()¶ Encode all options in option header into string of bytes.
-
add_option
(option)¶ Add option into option header.
-
delete_option
(number)¶ Delete option from option header.
-
get_option
(number)¶ Get option with specified number.
-
option_list
()¶
-
uri_path
¶ Iterable view on the URI_PATH option.
-
uri_query
¶ Iterable view on the URI_QUERY option.
-
location_path
¶ Iterable view on the LOCATION_PATH option.
-
location_query
¶ Iterable view on the LOCATION_QUERY option.
-
block2
¶ Single-value view on the BLOCK2 option.
-
block1
¶ Single-value view on the BLOCK1 option.
-
content_format
¶ Single-value view on the CONTENT_FORMAT option.
-
etag
¶ Single ETag as used in responses
List of ETags as used in requests
-
observe
¶ Single-value view on the OBSERVE option.
-
accept
¶ Single-value view on the ACCEPT option.
-
uri_host
¶ Single-value view on the URI_HOST option.
-
uri_port
¶ Single-value view on the URI_PORT option.
-
proxy_uri
¶ Single-value view on the PROXY_URI option.
-
proxy_scheme
¶ Single-value view on the PROXY_SCHEME option.
-
size1
¶ Single-value view on the SIZE1 option.
-
aiocoap.interfaces module¶
This module provides interface base classes to various aiocoap services, especially with respect to request and response handling.
-
class
aiocoap.interfaces.
TransportEndpoint
¶ Bases:
object
-
shutdown
()¶ Deactivate the complete transport, usually irrevertably. When the coroutine returns, the object must have made sure that it can be destructed by means of ref-counting or a garbage collector run.
-
send
(message)¶ Send a given
Message
object
-
fill_remote
(message)¶ Populate a message’s remote property based on its .opt.uri_host or .unresolved_remote. This interface is likely to change.
-
-
class
aiocoap.interfaces.
RequestProvider
¶ Bases:
object
-
class
aiocoap.interfaces.
Request
¶ Bases:
object
A CoAP request, initiated by sending a message. Typically, this is not instanciated directly, but generated by a
RequestProvider.request()
method.-
response
= 'A future that is present from the creation of the object and fullfilled with the response message.'¶
-
-
class
aiocoap.interfaces.
Resource
¶ Bases:
object
Interface that is expected by a
protocol.Context
to be present on the serversite, which renders all requests to that context.-
render
(request)¶ Return a message that can be sent back to the requester.
This does not need to set any low-level message options like remote, token or message type; it does however need to set a response code.
-
needs_blockwise_assembly
(request)¶ Indicator to the
protocol.Responder
about whether it should assemble request blocks to a single request and extract the requested blocks from a complete-resource answer (True), or whether the resource will do that by itself (False).
-
-
class
aiocoap.interfaces.
ObservableResource
¶ Bases:
aiocoap.interfaces.Resource
Interface the
protocol.ServerObservation
uses to negotiate whether an observation can be established based on a request.This adds only functionality for registering and unregistering observations; the notification contents will be retrieved from the resource using the regular
render()
method from crafted (fake) requests.-
add_observation
(request, serverobservation)¶ Before the incoming request is sent to
render()
, theadd_observation()
method is called. If the resource chooses to accept the observation, it has to call the serverobservation.accept(cb) with a callback that will be called when the observation ends. After accepting, the ObservableResource should call serverobservation.trigger() whenever it changes its state; the ServerObservation will then initiate notifications by having the request rendered again.
-
aiocoap.transports module¶
Container module for transports
Transports are expected to be the modular backends of aiocoap, and implement
the specifics of eg. TCP, WebSockets or SMS, possibly divided by backend
implementations as well. (If, for example, a non-posix platform is added, it
might be easier to rewrite the udp6
for that platform
instead of “ifdef hell”).
aiocoap.transports.udp6 module¶
This module implements a TransportEndpoint for UDP based on the asyncio DatagramProtocol.
As this makes use of RFC 3542 options (IPV6_PKTINFO), this is likely to only work with IPv6 interfaces. Hybrid stacks are supported, though, so V4MAPPED addresses (a la ::ffff:127.0.0.1) will be used when name resolution shows that a name is only available on V4.
-
class
aiocoap.transports.udp6.
UDP6EndpointAddress
(sockaddr, *, pktinfo=None)¶ Bases:
object
-
hostinfo
¶
-
port
¶
-
is_multicast
¶
-
-
class
aiocoap.transports.udp6.
SockExtendedErr
¶ Bases:
aiocoap.transports.udp6._SockExtendedErr
-
classmethod
load
(data)¶
-
classmethod
-
class
aiocoap.transports.udp6.
TransportEndpointUDP6
(new_message_callback, new_error_callback, log, loop)¶ Bases:
aiocoap.util.asyncio.RecvmsgDatagramProtocol
,aiocoap.interfaces.TransportEndpoint
-
ready
= None¶ Future that gets fullfilled by connection_made (ie. don’t send before this is done; handled by
create_..._context
-
classmethod
create_client_transport_endpoint
(new_message_callback, new_error_callback, log, loop, dump_to)¶
-
classmethod
create_server_transport_endpoint
(new_message_callback, new_error_callback, log, loop, dump_to, bind)¶
-
shutdown
()¶
-
send
(message)¶
-
fill_remote
(request)¶
-
connection_made
(transport)¶ Implementation of the DatagramProtocol interface, called by the transport.
-
datagram_msg_received
(data, ancdata, flags, address)¶ Implementation of the RecvmsgDatagramProtocol interface, called by the transport.
-
datagram_errqueue_received
(data, ancdata, flags, address)¶
-
error_received
(exc)¶ Implementation of the DatagramProtocol interface, called by the transport.
-
connection_lost
(exc)¶
-
aiocoap.proxy module¶
Container module, see submodules:
aiocoap.proxy.client module¶
-
class
aiocoap.proxy.client.
ProxyForwarder
(proxy_address, context)¶ Bases:
aiocoap.interfaces.RequestProvider
Object that behaves like a Context but only provides the request function and forwards all messages to a proxy.
This is not a proxy itself, it is just the interface for an external one.
-
proxy
¶
-
request
(message, **kwargs)¶
-
-
class
aiocoap.proxy.client.
ProxyRequest
(proxy, app_request, exchange_monitor_factory=<function ProxyRequest.<lambda>>)¶ Bases:
aiocoap.interfaces.Request
-
class
aiocoap.proxy.client.
ProxyClientObservation
(original_request)¶ Bases:
aiocoap.protocol.ClientObservation
-
real_observation
= None¶
-
cancel
()¶
-
aiocoap.proxy.server module¶
Basic implementation of CoAP-CoAP proxying
This is work in progress and not yet part of the API.
-
exception
aiocoap.proxy.server.
CanNotRedirect
(code, explanation)¶ Bases:
Exception
-
exception
aiocoap.proxy.server.
CanNotRedirectBecauseOfUnsafeOptions
(options)¶
-
aiocoap.proxy.server.
raise_unless_safe
(request, known_options)¶ Raise a BAD_OPTION CanNotRedirect unless all options in request are safe to forward or known
-
class
aiocoap.proxy.server.
Proxy
(outgoing_context, logger=None)¶ Bases:
aiocoap.interfaces.Resource
-
interpret_block_options
= False¶
-
add_redirector
(redirector)¶
-
apply_redirection
(request)¶
-
needs_blockwise_assembly
(request)¶
-
render
(request)¶
-
-
class
aiocoap.proxy.server.
ProxyWithPooledObservations
(outgoing_context, logger=None)¶ Bases:
aiocoap.proxy.server.Proxy
,aiocoap.interfaces.ObservableResource
-
add_observation
(request, serverobservation)¶ As ProxiedResource is intended to be just the proxy’s interface toward the Context, accepting observations is handled here, where the observations handling can be defined by the subclasses.
-
render
(request)¶
-
-
class
aiocoap.proxy.server.
ForwardProxy
(outgoing_context, logger=None)¶ Bases:
aiocoap.proxy.server.Proxy
-
apply_redirection
(request)¶
-
-
class
aiocoap.proxy.server.
ForwardProxyWithPooledObservations
(outgoing_context, logger=None)¶ Bases:
aiocoap.proxy.server.ForwardProxy
,aiocoap.proxy.server.ProxyWithPooledObservations
-
class
aiocoap.proxy.server.
ReverseProxy
(outgoing_context, logger=None)¶ Bases:
aiocoap.proxy.server.Proxy
-
apply_redirection
(request)¶
-
-
class
aiocoap.proxy.server.
ReverseProxyWithPooledObservations
(outgoing_context, logger=None)¶ Bases:
aiocoap.proxy.server.ReverseProxy
,aiocoap.proxy.server.ProxyWithPooledObservations
-
aiocoap.proxy.server.
splitport
(hostport)¶ Like urllib.parse.splitport, but return port as int, and as None if it equals the CoAP default port. Also, it allows giving IPv6 addresses like a netloc:
>>> splitport('foo') ('foo', None) >>> splitport('foo:5683') ('foo', None) >>> splitport('[::1]:56830') ('::1', 56830)
-
class
aiocoap.proxy.server.
NameBasedVirtualHost
(match_name, target, rewrite_uri_host=False)¶ Bases:
aiocoap.proxy.server.Redirector
-
apply_redirection
(request)¶
-
-
class
aiocoap.proxy.server.
UnconditionalRedirector
(target)¶ Bases:
aiocoap.proxy.server.Redirector
-
apply_redirection
(request)¶
-
-
class
aiocoap.proxy.server.
SubresourceVirtualHost
(path, target)¶ Bases:
aiocoap.proxy.server.Redirector
-
apply_redirection
(request)¶
-
aiocoap.cli module¶
Container module for command line utilities bundled with aiocoap:
proxy
– running a stand-alone forward or reverse proxy serverclient
– a CoAP client similar to curl
Be warned that even though these modules are documented here, they are not considered to be a part of the aioCoAP API, and are thus subject to change even when the project reaches a stable version number. If you want to use any of that infrastructure, please file a feature request for stabilization in the project’s issue tracker.
aiocoap.cli.client module¶
aiocoap-client is a simple command-line tool for interacting with CoAP servers
-
aiocoap.cli.client.
parse_commandline
(args)¶
-
aiocoap.cli.client.
configure_logging
(verbosity)¶
-
aiocoap.cli.client.
incoming_observation
(options, response)¶
-
aiocoap.cli.client.
single_request
(args, context=None)¶
-
aiocoap.cli.client.
interactive
()¶
-
aiocoap.cli.client.
sync_main
(args=None)¶
aiocoap.cli.proxy module¶
a plain CoAP proxy that can work both as forward and as reverse proxy
-
aiocoap.cli.proxy.
parse_commandline
(args)¶
-
class
aiocoap.cli.proxy.
Main
(*args, **kwargs)¶ Bases:
aiocoap.util.cli.AsyncCLIDaemon
-
start
(args=None)¶
-
shutdown
()¶
-
aiocoap.numbers module¶
Module in which all meaningful numbers are collected. Most of the submodules correspond to IANA registries.
aiocoap.numbers.codes module¶
List of known values for the CoAP “Code” field.
The values in this module correspond to the IANA registry “CoRE Parameters”, subregistries “CoAP Method Codes” and “CoAP Response Codes”.
The codes come with methods that can be used to get their rough meaning, see
the Code
class for details.
-
class
aiocoap.numbers.codes.
Code
¶ Bases:
aiocoap.util.ExtensibleIntEnum
Value for the CoAP “Code” field.
As the number range for the code values is separated, the rough meaning of a code can be determined using the
is_request()
,is_response()
andis_successful()
methods.-
EMPTY
= <Code 0 "EMPTY">¶
-
GET
= <Request Code 1 "GET">¶
-
POST
= <Request Code 2 "POST">¶
-
PUT
= <Request Code 3 "PUT">¶
-
DELETE
= <Request Code 4 "DELETE">¶
-
FETCH
= <Request Code 5 "FETCH">¶
-
PATCH
= <Request Code 6 "PATCH">¶
-
iPATCH
= <Request Code 7 "iPATCH">¶
-
CREATED
= <Successful Response Code 65 "2.01 Created">¶
-
DELETED
= <Successful Response Code 66 "2.02 Deleted">¶
-
VALID
= <Successful Response Code 67 "2.03 Valid">¶
-
CHANGED
= <Successful Response Code 68 "2.04 Changed">¶
-
CONTENT
= <Successful Response Code 69 "2.05 Content">¶
-
CONTINUE
= <Successful Response Code 95 "2.31 Continue">¶
-
BAD_REQUEST
= <Response Code 128 "4.00 Bad Request">¶
-
UNAUTHORIZED
= <Response Code 129 "4.01 Unauthorized">¶
-
BAD_OPTION
= <Response Code 130 "4.02 Bad Option">¶
-
FORBIDDEN
= <Response Code 131 "4.03 Forbidden">¶
-
NOT_FOUND
= <Response Code 132 "4.04 Not Found">¶
-
METHOD_NOT_ALLOWED
= <Response Code 133 "4.05 Method Not Allowed">¶
-
NOT_ACCEPTABLE
= <Response Code 134 "4.06 Not Acceptable">¶
-
REQUEST_ENTITY_INCOMPLETE
= <Response Code 136 "4.08 Request Entity Incomplete">¶
-
CONFLICT
= <Response Code 137 "4.09 Conflict">¶
-
PRECONDITION_FAILED
= <Response Code 140 "4.12 Precondition Failed">¶
-
REQUEST_ENTITY_TOO_LARGE
= <Response Code 141 "4.13 Request Entity Too Large">¶
-
UNSUPPORTED_MEDIA_TYPE
= <Response Code 143 "4.15 Unsupported Media Type">¶
-
UNPROCESSABLE_ENTITY
= <Response Code 150 "4.22 Unprocessable Entity">¶
-
INTERNAL_SERVER_ERROR
= <Response Code 160 "5.00 Internal Server Error">¶
-
NOT_IMPLEMENTED
= <Response Code 161 "5.01 Not Implemented">¶
-
BAD_GATEWAY
= <Response Code 162 "5.02 Bad Gateway">¶
-
SERVICE_UNAVAILABLE
= <Response Code 163 "5.03 Service Unavailable">¶
-
GATEWAY_TIMEOUT
= <Response Code 164 "5.04 Gateway Timeout">¶
-
PROXYING_NOT_SUPPORTED
= <Response Code 165 "5.05 Proxying Not Supported">¶
-
is_request
()¶ True if the code is in the request code range
-
is_response
()¶ True if the code is in the response code range
-
is_successful
()¶ True if the code is in the successful subrange of the response code range
-
dotted
¶ The numeric value three-decimal-digits (c.dd) form
-
name_printable
¶ The name of the code in human-readable form
-
name
¶ The constant name of the code (equals name_printable readable in all-caps and with underscores)
-
aiocoap.numbers.constants module¶
Constants either defined in the CoAP protocol (often default values for lack of ways to determine eg. the estimated round trip time). Some parameters are invented here for practical purposes of the implementation (eg. DEFAULT_BLOCK_SIZE_EXP, EMPTY_ACK_DELAY).
-
aiocoap.numbers.constants.
COAP_PORT
= 5683¶ The IANA-assigned standard port for COAP services.
-
aiocoap.numbers.constants.
ACK_TIMEOUT
= 2.0¶ The time, in seconds, to wait for an acknowledgement of a confirmable message. The inter-transmission time doubles for each retransmission.
-
aiocoap.numbers.constants.
ACK_RANDOM_FACTOR
= 1.5¶ Timeout multiplier for anti-synchronization.
-
aiocoap.numbers.constants.
MAX_RETRANSMIT
= 4¶ The number of retransmissions of confirmable messages to non-multicast endpoints before the infrastructure assumes no acknowledgement will be received.
-
aiocoap.numbers.constants.
NSTART
= 1¶ Maximum number of simultaneous outstanding interactions that endpoint maintains to a given server (including proxies)
-
aiocoap.numbers.constants.
MAX_TRANSMIT_SPAN
= 45.0¶ Maximum time from the first transmission of a confirmable message to its last retransmission.
-
aiocoap.numbers.constants.
MAX_TRANSMIT_WAIT
= 93.0¶ Maximum time from the first transmission of a confirmable message to the time when the sender gives up on receiving an acknowledgement or reset.
-
aiocoap.numbers.constants.
MAX_LATENCY
= 100.0¶ Maximum time a datagram is expected to take from the start of its transmission to the completion of its reception.
-
aiocoap.numbers.constants.
PROCESSING_DELAY
= 2.0¶ “Time a node takes to turn around a confirmable message into an acknowledgement.
-
aiocoap.numbers.constants.
MAX_RTT
= 202.0¶ Maximum round-trip time.
-
aiocoap.numbers.constants.
EXCHANGE_LIFETIME
= 247.0¶ time from starting to send a confirmable message to the time when an acknowledgement is no longer expected, i.e. message layer information about the message exchange can be purged
-
aiocoap.numbers.constants.
DEFAULT_BLOCK_SIZE_EXP
= 6¶ Default size exponent for blockwise transfers.
-
aiocoap.numbers.constants.
EMPTY_ACK_DELAY
= 0.1¶ After this time protocol sends empty ACK, and separate response
-
aiocoap.numbers.constants.
REQUEST_TIMEOUT
= 93.0¶ Time after which server assumes it won’t receive any answer. It is not defined by IETF documents. For human-operated devices it might be preferable to set some small value (for example 10 seconds) For M2M it’s application dependent.
aiocoap.numbers.optionnumbers module¶
Known values for CoAP option numbers
The values defined in OptionNumber correspond to the IANA registry “CoRE Parameters”, subregistries “CoAP Method Codes” and “CoAP Response Codes”.
The option numbers come with methods that can be used to evaluate their properties, see the OptionNumber class for details.
-
class
aiocoap.numbers.optionnumbers.
OptionNumber
¶ Bases:
aiocoap.util.ExtensibleIntEnum
A CoAP option number.
As the option number contains information on whether the option is critical, and whether it is safe-to-forward, those properties can be queried using the is_* group of methods.
Note that whether an option may be repeated or not does not only depend on the option, but also on the context, and is thus handled in the Options object instead.
-
IF_MATCH
= <OptionNumber 1 "IF_MATCH">¶
-
URI_HOST
= <OptionNumber 3 "URI_HOST">¶
-
ETAG
= <OptionNumber 4 "ETAG">¶
-
IF_NONE_MATCH
= <OptionNumber 5 "IF_NONE_MATCH">¶
-
OBSERVE
= <OptionNumber 6 "OBSERVE">¶
-
URI_PORT
= <OptionNumber 7 "URI_PORT">¶
-
LOCATION_PATH
= <OptionNumber 8 "LOCATION_PATH">¶
-
URI_PATH
= <OptionNumber 11 "URI_PATH">¶
-
CONTENT_FORMAT
= <OptionNumber 12 "CONTENT_FORMAT">¶
-
MAX_AGE
= <OptionNumber 14 "MAX_AGE">¶
-
URI_QUERY
= <OptionNumber 15 "URI_QUERY">¶
-
ACCEPT
= <OptionNumber 17 "ACCEPT">¶
-
LOCATION_QUERY
= <OptionNumber 20 "LOCATION_QUERY">¶
-
BLOCK2
= <OptionNumber 23 "BLOCK2">¶
-
BLOCK1
= <OptionNumber 27 "BLOCK1">¶
-
SIZE2
= <OptionNumber 28 "SIZE2">¶
-
PROXY_URI
= <OptionNumber 35 "PROXY_URI">¶
-
PROXY_SCHEME
= <OptionNumber 39 "PROXY_SCHEME">¶
-
SIZE1
= <OptionNumber 60 "SIZE1">¶
-
is_critical
()¶
-
is_elective
()¶
-
is_unsafe
()¶
-
is_safetoforward
()¶
-
is_nocachekey
()¶
-
is_cachekey
()¶
-
format
¶
-
create_option
(decode=None, value=None)¶ Return an Option element of the appropriate class from this option number.
An initial value may be set using the decode or value options, and will be fed to the resulting object’s decode method or value property, respectively.
-
aiocoap.error module¶
Exception definitions for txThings CoAP library.
-
exception
aiocoap.error.
Error
¶ Bases:
Exception
Base exception for all exceptions that indicate a failed request
-
exception
aiocoap.error.
RenderableError
¶ Bases:
aiocoap.error.Error
Exception that can meaningfully be represented in a CoAP response
-
to_message
()¶ Create a CoAP message that should be sent when this exception is rendered
-
-
exception
aiocoap.error.
ResponseWrappingError
(coapmessage)¶ Bases:
aiocoap.error.Error
An exception that is raised due to an unsuccessful but received response.
A better relationship with
numbers.codes
should be worked out to do except UnsupportedMediaType.-
to_message
()¶
-
-
exception
aiocoap.error.
ConstructionRenderableError
¶ Bases:
aiocoap.error.RenderableError
RenderableError that is constructed from class attrinutes
code
andmessage
-
code
= <Response Code 160 "5.00 Internal Server Error">¶
-
message
= ''¶
-
-
exception
aiocoap.error.
NotFound
¶ Bases:
aiocoap.error.ConstructionRenderableError
-
code
= <Response Code 132 "4.04 Not Found">¶
-
-
exception
aiocoap.error.
MethodNotAllowed
¶ Bases:
aiocoap.error.ConstructionRenderableError
-
code
= <Response Code 133 "4.05 Method Not Allowed">¶
-
-
exception
aiocoap.error.
UnsupportedMediaType
¶ Bases:
aiocoap.error.ConstructionRenderableError
-
code
= <Response Code 143 "4.15 Unsupported Media Type">¶
-
-
exception
aiocoap.error.
BadRequest
¶ Bases:
aiocoap.error.ConstructionRenderableError
-
code
= <Response Code 128 "4.00 Bad Request">¶
-
-
exception
aiocoap.error.
NoResource
¶ Bases:
aiocoap.error.NotFound
Raised when resource is not found.
-
message
= 'Error: Resource not found!'¶
-
-
exception
aiocoap.error.
UnallowedMethod
¶ Bases:
aiocoap.error.MethodNotAllowed
Raised by a resource when request method is understood by the server but not allowed for that particular resource.
-
message
= 'Error: Method not allowed!'¶
-
-
exception
aiocoap.error.
UnsupportedMethod
¶ Bases:
aiocoap.error.MethodNotAllowed
Raised when request method is not understood by the server at all.
-
message
= 'Error: Method not recognized!'¶
-
-
exception
aiocoap.error.
NotImplemented
¶ Bases:
aiocoap.error.Error
Raised when request is correct, but feature is not implemented by txThings library. For example non-sequential blockwise transfers
-
exception
aiocoap.error.
RequestTimedOut
¶ Bases:
aiocoap.error.Error
Raised when request is timed out.
-
exception
aiocoap.error.
WaitingForClientTimedOut
¶ Bases:
aiocoap.error.Error
Raised when server expects some client action:
- sending next PUT/POST request with block1 or block2 option
- sending next GET request with block2 option
but client does nothing.
-
exception
aiocoap.error.
ResourceChanged
¶ Bases:
aiocoap.error.Error
The requested resource was modified during the request and could therefore not be received in a consistent state.
-
exception
aiocoap.error.
UnexpectedBlock1Option
¶ Bases:
aiocoap.error.Error
Raised when a server responds with block1 options that just don’t match.
-
exception
aiocoap.error.
UnexpectedBlock2
¶ Bases:
aiocoap.error.Error
Raised when a server responds with another block2 than expected.
-
exception
aiocoap.error.
MissingBlock2Option
¶ Bases:
aiocoap.error.Error
Raised when response with Block2 option is expected (previous response had Block2 option with More flag set), but response without Block2 option is received.
-
exception
aiocoap.error.
NotObservable
¶ Bases:
aiocoap.error.Error
The server did not accept the request to observe the resource.
-
exception
aiocoap.error.
ObservationCancelled
¶ Bases:
aiocoap.error.Error
The server claimed that it will no longer sustain the observation.
-
exception
aiocoap.error.
UnparsableMessage
¶ Bases:
aiocoap.error.Error
An incoming message does not look like CoAP.
Note that this happens rarely – the requirements are just two bit at the beginning of the message, and a minimum length.
-
exception
aiocoap.error.
CommunicationKilled
¶ Bases:
aiocoap.error.ConstructionRenderableError
The communication process has been aborted by request of the application.
-
code
= <Response Code 163 "5.03 Service Unavailable">¶
-
aiocoap.optiontypes module¶
-
class
aiocoap.optiontypes.
OptionType
(number, value)¶ Bases:
object
Interface for decoding and encoding option values
Instances of
OptionType
are collected in a list in aMessage.opt
Options
object, and provide a translation between the CoAP octet-stream (accessed using theencode()
/decode()
method pair) and the interpreted value (accessed via thevalue
attribute).Note that OptionType objects usually don’t need to be handled by library users; the recommended way to read and set options is via the Options object’sproperties (eg.
message.opt.uri_path = ('.well-known', 'core')
).-
encode
()¶ Return the option’s value in serialzied form
-
decode
(rawdata)¶ Set the option’s value from the bytes in rawdata
-
length
¶ Indicate the length of the encoded value
-
-
class
aiocoap.optiontypes.
StringOption
(number, value='')¶ Bases:
aiocoap.optiontypes.OptionType
String CoAP option - used to represent string options. Always encoded in UTF8 per CoAP specification.
-
encode
()¶
-
decode
(rawdata)¶
-
length
¶
-
-
class
aiocoap.optiontypes.
OpaqueOption
(number, value=b'')¶ Bases:
aiocoap.optiontypes.OptionType
Opaque CoAP option - used to represent options that just have their uninterpreted bytes as value.
-
encode
()¶
-
decode
(rawdata)¶
-
length
¶
-
-
class
aiocoap.optiontypes.
UintOption
(number, value=0)¶ Bases:
aiocoap.optiontypes.OptionType
Uint CoAP option - used to represent integer options.
-
encode
()¶
-
decode
(rawdata)¶
-
length
¶
-
-
class
aiocoap.optiontypes.
BlockOption
(number, value=None)¶ Bases:
aiocoap.optiontypes.OptionType
Block CoAP option - special option used only for Block1 and Block2 options. Currently it is the only type of CoAP options that has internal structure.
-
BlockOption.
value
¶
-
BlockOption.
encode
()¶
-
BlockOption.
decode
(rawdata)¶
-
BlockOption.
length
¶
-
aiocoap.resource module¶
Basic resource implementations
A resource in URL / CoAP / REST terminology is the thing identified by a URI.
Here, a Resource
is the place where server functionality is
implemented. In many cases, there exists one persistent Resource object for a
given resource (eg. a TimeResource()
is responsible for serving the
/time
location). On the other hand, an aiocoap server context accepts only
one thing as its serversite, and that is a Resource too (typically of the
Site
class).
Resources are most easily implemented by deriving from Resource
and
implementing render_get
, render_post
and similar coroutine methods.
Those take a single request message object and must return a
aiocoap.Message
object or raise an
error.RenderableError
(eg. raise UnsupportedMediaType()
).
To serve more than one resource on a site, use the Site
class to
dispatch requests based on the Uri-Path header.
-
aiocoap.resource.
hashing_etag
(request, response)¶ Helper function for render_get handlers that allows them to use ETags based on the payload’s hash value
Run this on your request and response before returning from render_get; it is safe to use this function with all kinds of responses, it will only act on 2.05 Content. The hash used are the first 8 bytes of the sha1 sum of the payload.
Note that this method is not ideal from a server performance point of view (a file server, for example, might want to hash only the stat() result of a file instead of reading it in full), but it saves bandwith for the simple cases.
>>> from aiocoap import * >>> req = Message(code=GET) >>> hash_of_hello = b'\xaa\xf4\xc6\x1d\xdc\xc5\xe8\xa2' >>> req.opt.etags = [hash_of_hello] >>> resp = Message(code=CONTENT) >>> resp.payload = b'hello' >>> hashing_etag(req, resp) >>> resp <aiocoap.Message at ... 2.03 Valid ... 1 option(s)>
-
class
aiocoap.resource.
Resource
¶ Bases:
aiocoap.resource._ExposesWellknownAttributes
,aiocoap.interfaces.Resource
Simple base implementation of the
interfaces.Resource
interfaceThe render method delegates content creation to
render_$method
methods, and responds appropriately to unsupported methods.Moreover, this class provides a
get_link_description
method as used by .well-known/core to expose a resource’s.ct
,.rt
and.if_
(alternative name forif
as that’s a Python keyword) attributes.-
needs_blockwise_assembly
(request)¶
-
render
(request)¶
-
-
class
aiocoap.resource.
ObservableResource
¶ Bases:
aiocoap.resource.Resource
,aiocoap.interfaces.ObservableResource
-
add_observation
(request, serverobservation)¶
-
update_observation_count
(newcount)¶ Hook into this method to be notified when the number of observations on the resource changes.
-
updated_state
(response=None)¶ Call this whenever the resource was updated, and a notification should be sent to observers.
-
get_link_description
()¶
-
-
class
aiocoap.resource.
WKCResource
(listgenerator)¶ Bases:
aiocoap.resource.Resource
Read-only dynamic resource list, suitable as .well-known/core.
This resource renders a link_header.LinkHeader object (which describes a collection of resources) as application/link-format (RFC 6690).
The list to be rendered is obtained from a function passed into the constructor; typically, that function would be a bound Site.get_resources_as_linkheader() method.
-
ct
= 40¶
-
render_get
(request)¶
-
-
class
aiocoap.resource.
PathCapable
¶ Bases:
object
Class that indicates that a resource promises to parse the uri_path option, and can thus be given requests for
render()
ing that contain a uri_path
-
class
aiocoap.resource.
Site
¶ Bases:
aiocoap.resource._ExposesWellknownAttributes
,aiocoap.interfaces.ObservableResource
,aiocoap.resource.PathCapable
Typical root element that gets passed to a
Context
and contains all the resources that can be found when the endpoint gets accessed as a server.This provides easy registration of statical resources.
Add resources at absolute locations using the
add_resource()
method. You can add another Site as well, those will be nested and integrally reported in a WKCResource. The path of a site should not end with an empty string (ie. a slash in the URI) – the child site’s own root resource will then have the trailing slash address.Resources added to a site will receive only messages that are directed to that very resource (ie.
/spam/eggs
will not receive requests for/spam/eggs/42
) unless they arePathCapable
(like another Site).-
needs_blockwise_assembly
(request)¶
-
render
(request)¶
-
add_observation
(request, serverobservation)¶
-
add_resource
(path, resource)¶
-
remove_resource
(path)¶
-
get_resources_as_linkheader
()¶
-
aiocoap.dump module¶
-
class
aiocoap.dump.
TextDumper
(outfile, protocol=None)¶ Bases:
aiocoap.util.asyncio.RecvmsgDatagramProtocol
Plain text network data dumper
A TextDumper can be used to log network traffic into a file that can be converted to a PCAP-NG file as described in its header.
Currently, this discards information like addresses; it is unknown how that information can be transferred into a dump reader easily while simultaneously staying at application level and staying ignorant of particular underlying protocols’ data structures.
It could previously be used stand-alone (outside of the asyncio transport/protocol mechanisms) when instanciated only with an output file (the
datagram_received()
andsendto()
were used), but with thedatagram_msg_received()
substitute method, this is probably impractical now.To use it between an asyncio transport and protocol, use the :meth:endpointfactory method.
-
classmethod
endpointfactory
(outfile, actual_protocol)¶ This method returns a function suitable for passing to an asyncio loop’s .create_datagram_endpoint method. It will place the TextDumper between the object and the transport, transparently dumping network traffic and passing it on together with other methods defined in the protocol/transport interface.
If you need the actual protocol after generating the endpoint (which when using this method returns a TextDumper instead of an actual_protocol), you can access it using the protocol property.
-
protocol
¶
-
datagram_msg_received
(data, ancdata, flags, address)¶
-
sendmsg
(data, ancdata, flags, address)¶
-
connection_made
(transport)¶
-
close
()¶
-
connection_lost
(exc)¶
-
classmethod
aiocoap.util module¶
Tools not directly related with CoAP that are needed to provide the API
-
class
aiocoap.util.
ExtensibleEnumMeta
(name, bases, dict)¶ Bases:
type
Metaclass for ExtensibleIntEnum, see there for detailed explanations
-
class
aiocoap.util.
ExtensibleIntEnum
¶ Bases:
int
Similar to Python3.4’s enum.IntEnum, this type can be used for named numbers which are not comprehensively known, like CoAP option numbers.
-
aiocoap.util.
hostportjoin
(host, port=None)¶ Join a host and optionally port into a hostinfo-style host:port string
aiocoap.util.asyncio module¶
asyncio workarounds
-
aiocoap.util.asyncio.
cancel_thoroughly
(handle)¶ Use this on a (Timer)Handle when you would .cancel() it, just also drop the callback and arguments for them to be freed soon.
-
class
aiocoap.util.asyncio.
RecvmsgDatagramProtocol
¶ Bases:
asyncio.protocols.DatagramProtocol
Inheriting from this indicates that the instance expects to be called back datagram_msg_received instead of datagram_received
aiocoap.util.cli module¶
Helpers for creating server-style applications in aiocoap
Note that these are not particular to aiocoap, but are used at different places in aiocoap and thus shared here.
-
class
aiocoap.util.cli.
AsyncCLIDaemon
(*args, **kwargs)¶ Bases:
object
Helper for creating daemon-style CLI prorgrams.
Note that this currently doesn’t create a Daemon in the sense of doing a daemon-fork; that could be added on demand, though.
Subclass this and implement the
start()
method as an async function; it will be passed all the constructor’s arguments.When all setup is complete and the program is operational, return from the start method.
Implement the
shutdown()
coroutine and to do cleanup; what actually runs your program will, if possible, call that and await its return.Typical application for this is running
MyClass.sync_main()
in the program’sif __name__ == "__main__":
section.-
classmethod
sync_main
(*args, **kwargs)¶ Run the application in an AsyncIO main loop, shutting down cleanly on keyboard interrupt.
-
classmethod
aiocoap.util.queuewithend module¶
This is a relic from before the __aiter__
protocol was established; it
will be phased out before aiocoap 1.0 is released.
-
class
aiocoap.util.queuewithend.
AsyncIterable
¶ Bases:
object
-
can_peek
()¶ Return True when a result is ready to be fetched with .get_nowait(), and False when no more items can be fetched.
-
get_nowait
()¶ Fetch the next item. This must only be called once after can_peek has returned True.
-
-
class
aiocoap.util.queuewithend.
QueueWithEnd
(maxsize=0)¶ Bases:
aiocoap.util.queuewithend.AsyncIterable
A QueueWithEnd shares a Queue’s behavior in that it gets fed with put and consumed with get_nowait. Contrary to a Queue, this is designed to be consumed only by one entity, which uses the coroutine can_peek to make sure the get_nowait will succeed.
Another difference between a Queue and a QueueWithEnd is that the latter can also terminate (which is indicated by can_peek returning False and set by the finish coroutine) and raise exceptions (which raise from the get_nowait function and are set by the put_exception coroutine).
-
Type
¶ alias of
QueueWithEnd.Type
-
can_peek
()¶
-
get_nowait
()¶
-
put
(value)¶
-
put_exception
(value)¶
-
finish
()¶
-
classmethod
cogenerator
(maxsize=0)¶ Coroutine decorator that passes a callable asyncyield into the function as the first argument and returns a QueueWithEnd. It is implicitly finished when the coroutine returns.
>>> @QueueWithEnd.cogenerator() >>> def count_slowly(asyncyield, count_to=count_to): ... for i in range(count_to): ... yield from asyncio.sleep(1) ... yield from asyncyield(i + 1) >>> counter = count_slowly(10) >>> while (yield from counter.can_peek()): ... i = counter.get_nowait() ... print("Current count is %d"%i)
-
classmethod
merge
(queues)¶ Asyncio’s as_completed does not work with QueueWithEnd objects for the same reason it can’t replace it (missing end-of-loop indication); the merge classmethod can be used instead to fetch results indiscriminately from queues as they are completed:
>>> @QueueWithEnd.cogenerator() >>> def count(asyncyield): ... for i in range(3): ... yield from asyncyield(i + 1) ... yield from time.sleep(0.1 * i) >>> firstcount = count() >>> secondcount = count() >>> merged = QueueWithEnd.merged([firstcount, secondcount]) >>> while (yield from merged.can_peek()): ... print(merged.get_nowait()) 1 2 1 2 3 3
-
more
()¶
-
value
¶
-
consume
()¶
-
aiocoap.util.socknumbers module¶
This module contains numeric constants that would be expected in the socket module, but are not exposed there.
For some platforms (eg. python up to 3.5 on Linux), there is an IN module that exposes them; and they are gathered from there.
As a fallback, the numbers are hardcoded. Any hints on where to get them from are appreciated; possible options are parsing C header files (at build time?) or interacting with shared libraries for obtaining the symbols. The right way would probably be including them in Python.
Usage Examples¶
In absence of a complete tutorial, these files can serve as reference
implementations for server and client. In order to test them, run
./server.py
in one terminal, and use ./clientGET.py
and
./clientPUT.py
to interact with it.
Unlike the library and its tools, these examples use the modern (Python 3.5 and
later) async
idiom instead of the original asyncio yield from
. This is
to align them better with what novice users are expected to learn when
introduced to asynchronous programming in Python.
Client¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import logging
import asyncio
from aiocoap import *
logging.basicConfig(level=logging.INFO)
async def main():
protocol = await Context.create_client_context()
request = Message(code=GET, uri='coap://localhost/time')
try:
response = await protocol.request(request).response
except Exception as e:
print('Failed to fetch resource:')
print(e)
else:
print('Result: %s\n%r'%(response.code, response.payload))
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import logging
import asyncio
from aiocoap import *
logging.basicConfig(level=logging.INFO)
async def main():
"""
Example class which performs single PUT request to localhost
port 5683 (official IANA assigned CoAP port), URI "/other/block".
Request is sent 2 seconds after initialization.
Payload is bigger than 1kB, and thus is sent as several blocks.
"""
context = await Context.create_client_context()
await asyncio.sleep(2)
payload = b"The quick brown fox jumps over the lazy dog.\n" * 30
request = Message(code=PUT, payload=payload)
request.opt.uri_host = '127.0.0.1'
request.opt.uri_path = ("other", "block")
response = await context.request(request).response
print('Result: %s\n%r'%(response.code, response.payload))
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
|
Server¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
import datetime
import logging
import asyncio
import aiocoap.resource as resource
import aiocoap
class BlockResource(resource.Resource):
"""
Example resource which supports GET and PUT methods. It sends large
responses, which trigger blockwise transfer.
"""
def __init__(self):
super(BlockResource, self).__init__()
self.content = ("This is the resource's default content. It is padded "\
"with numbers to be large enough to trigger blockwise "\
"transfer.\n" + "0123456789\n" * 100).encode("ascii")
async def render_get(self, request):
return aiocoap.Message(payload=self.content)
async def render_put(self, request):
print('PUT payload: %s' % request.payload)
self.content = request.payload
payload = ("I've accepted the new payload. You may inspect it here in "\
"Python's repr format:\n\n%r"%self.content).encode('utf8')
return aiocoap.Message(payload=payload)
class SeparateLargeResource(resource.Resource):
"""
Example resource which supports GET method. It uses asyncio.sleep to
simulate a long-running operation, and thus forces the protocol to send
empty ACK first.
"""
def __init__(self):
super(SeparateLargeResource, self).__init__()
# self.add_param(resource.LinkParam("title", "Large resource."))
async def render_get(self, request):
await asyncio.sleep(3)
payload = "Three rings for the elven kings under the sky, seven rings"\
"for dwarven lords in their halls of stone, nine rings for"\
"mortal men doomed to die, one ring for the dark lord on his"\
"dark throne.".encode('ascii')
return aiocoap.Message(payload=payload)
class TimeResource(resource.ObservableResource):
"""
Example resource that can be observed. The `notify` method keeps scheduling
itself, and calles `update_state` to trigger sending notifications.
"""
def __init__(self):
super(TimeResource, self).__init__()
self.notify()
def notify(self):
self.updated_state()
asyncio.get_event_loop().call_later(6, self.notify)
def update_observation_count(self, count):
if count:
# not that it's actually implemented like that here -- unconditional updating works just as well
print("Keeping the clock nearby to trigger observations")
else:
print("Stowing away the clock until someone asks again")
async def render_get(self, request):
payload = datetime.datetime.now().strftime("%Y-%m-%d %H:%M").encode('ascii')
return aiocoap.Message(payload=payload)
#class CoreResource(resource.Resource):
# """
# Example Resource that provides list of links hosted by a server.
# Normally it should be hosted at /.well-known/core
#
# Notice that self.visible is not set - that means that resource won't
# be listed in the link format it hosts.
# """
#
# def __init__(self, root):
# resource.Resource.__init__(self)
# self.root = root
#
# async def render_get(self, request):
# data = []
# self.root.generate_resource_list(data, "")
# payload = ",".join(data).encode('utf-8')
# return aiocoap.Message(payload=payload, content_format=40)
# logging setup
logging.basicConfig(level=logging.INFO)
logging.getLogger("coap-server").setLevel(logging.DEBUG)
def main():
# Resource tree creation
root = resource.Site()
root.add_resource(('.well-known', 'core'), resource.WKCResource(root.get_resources_as_linkheader))
root.add_resource(('time',), TimeResource())
root.add_resource(('other', 'block'), BlockResource())
root.add_resource(('other', 'separate'), SeparateLargeResource())
asyncio.Task(aiocoap.Context.create_server_context(root))
asyncio.get_event_loop().run_forever()
if __name__ == "__main__":
main()
|
CoAP tools¶
As opposed to the Usage Examples, programs listed here are not tuned to show the use of aiocoap, but are tools for everyday work with CoAP implemented in aiocoap. Still, they can serve as examples of how to deal with user-provided addresses (as opposed to the fixed addresses in the examples), or of integration in a bigger project in general.
aiocoap-client
: A CoAP client that supports observations and proxying.aiocoap-proxy
: A command-line configurable forward and reverse proxy.
Those utilities are installed by setup.py at the usual executable locations;
during development or when working from a git checkout of the project, wrapper
scripts are available in the root directory. In some instances, it might be
practical to access their functionality from within Python; see the
aiocoap.cli
module documentation for details.
All tools provide details on their invocation and arguments when called with
the --help
option.
contrib¶
Tools in the contrib/
folder are somewhere inbetween Usage Examples and
the tools above; the rough idea is that they should be generally useful but not
necessarily production tools, and simple enough to be useful as an inspiration
for writing other tools; none of this is set in stone, though, so that area can
serve as a noncommittal playground.
There is currently onely one tool in there:
aiocoap-fileserver
: Serves the current directory’s contents as CoAP resources, implementing directory listing and observation. No write support yet.
Change log¶
This summarizes the changes between released versions. For a complete change log, see the git history. For details on the changes, see the respective git commits indicated at the start of the entry.
Version 0.3¶
Features¶
- 4d07615: ICMP errors are handled
- 1b61a29: Accept ‘fe80::...%eth0’ style addresses
- 3c0120a: Observations provide modern
async for
interface - 4e4ff7c: New demo: file server
- ef2e45e, 991098b, 684ccdd: Messages can be constructed with options,
modified copies can be created with the
.copy
method, and default codes are provided - 08845f2: Request objects have
.response_nonraising
and.response_raising
interfaces for easier error handling - ab5b88a, c49b5c8: Sites can be nested by adding them to an existing site, catch-all resources can be created by subclassing PathCapable
Possibly breaking changes¶
- ab5b88a: Site nesting means that server resources do not get their original Uri-Path any more
- bc76a7c: Location-{Path,Query} were opaque (bytes) objects instead of strings; disctinction between accidental and intentional opaque options is now clarified
Small features¶
- 2bb645e: set_request_uri allows URI parsing without sending Uri-Host
- e6b4839: Take block1.size_exponent as a sizing hint when sending block1 data
- 9eafd41: Allow passing in a loop into context creation
- 9ae5bdf: ObservableResource: Add update_observation_count
- c9f21a6: Stop client-side observations when unused
- dd46682: Drop dependency on obscure built-in IN module
- a18c067: Add numbers from draft-ietf-core-etch-04
- fabcfd5: .well-known/core supports filtering
Internals¶
- f968d3a: All low-level networking is now done in aiocoap.transports; it’s not really hotpluggable yet and only UDPv6 (with implicit v4 support) is implemented, but an extension point for alternative transports.
- bde8c42: recvmsg is used instead of recvfrom, requiring some asyncio hacks
Package management¶
- 01f7232, 0a9d03c: aiocoap-client and -proxy are entry points
- 0e4389c: Establish an extra requirement for LinkHeader
LICENSE¶
- Copyright (c) 2012-2014 Maciej Wasilak <http://sixpinetrees.blogspot.com/>,
- 2013-2014 Christian Amsüss <c.amsuess@energyharvesting.at>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.