zoap

A WiP CoAP implementation for bare-metal constrained devices in Zig

git clone https://git.8pit.net/zoap.git

  1# zoap
  2
  3A WiP [CoAP][rfc 7252] implementation for bare-metal [constrained devices][rfc 7228] in [Zig][zig web].
  4
  5## Status
  6
  7Presently, the majority of the CoAP standard is not implemented.
  8However, creating a very basic CoAP server which sends and receives
  9non-confirmable messages is possible and already done as part of my
 10[zig-riscv-embedded][zig-riscv github] project. Since the code focus
 11on constrained bare-metal targets, it is optimized for a small memory
 12footprint and uses statically allocated fixed-size buffers instead of
 13performing dynamic memory allocation. Furthermore, it does not use any
 14OS-specific code from the Zig standard library (e.g. Sockets).
 15
 16The code is known to compile with Zig `0.9.1`.
 17
 18## Installation
 19
 20Zig packages are simply Zig source trees and are imported using
 21[`@import`][zig import] just like code from the Zig standard library.
 22Therefore, the zoap source tree must be added to the Zig codebase using
 23it. This can, for example, be achieved using [git submodules][git submodules]
 24or a third-party package manager like [gyro][gyro github].
 25
 26For the former method, the package source tree needs to be explicitly
 27added to `build.rs`. Assuming, the submodule was added as `./zoap` in
 28the directory root the following code should be sufficient:
 29
 30	exe.addPackage(std.build.Pkg{
 31	    .name = "zoap",
 32	    .path = "./zoap/src/zoap.zig",
 33	});
 34
 35Afterwards, simply import zoap using `const zoap = @import("zoap");`.
 36
 37## Usage
 38
 39As noted above, this library targets freestanding constrained devices.
 40For this reason, all memory is statically allocated. To implement a CoAP
 41server with zoap, the central data structure is the Dispatcher. This
 42Dispatcher takes a list of Resources and forwards incoming requests to
 43them if the URI in the request matches one of the available resources.
 44Both, the dispatcher and the resources need to be statically allocated,
 45e.g. as global variables:
 46
 47	const resources = &[_]zoap.Resource{
 48	    .{ .path = "hello", .handler = helloHandler },
 49	    .{ .path = "about", .handler = aboutHandler },
 50	};
 51	var dispatcher = zoap.Dispatcher{
 52	    .resources = resources,
 53	};
 54
 55The code above allocates a dispatcher with two resources: `/hello` and
 56`/about`. An incoming CoAP request for either of those resources invokes
 57the associated handler function. The `helloHandler` implementation may
 58looks as follows:
 59
 60	pub fn helloHandler(resp: *zoap.Response, req: *zoap.Request) codes.Code {
 61	    if (!req.header.code.equal(codes.GET))
 62	        return codes.BAD_METHOD;
 63	
 64	    const w = resp.payloadWriter();
 65	    w.writeAll("Hello, World!") catch {
 66	        return codes.INTERNAL_ERR;
 67	    };
 68	
 69	    return codes.CONTENT;
 70	}
 71
 72The function takes two parameters: The resulting CoAP response and the
 73incoming CoAP request. The handler returns the CoAP response code for
 74the incoming request. The implementation above first checks the request
 75method, if it doesn't match the expected method a response with a
 76Method Not Allowed status code is returned. Otherwise, the
 77`helloHandler` writes `Hello, World!` to the response body and, unless an
 78error occurs, it responses with a successful content response code.
 79
 80In order to invoke these handlers, incoming CoAP requests need to be
 81forwarded to the Dispatcher via the `Dispatcher.dispatch` method which
 82takes an incoming CoAP request as a parameter and forwards it to the
 83matching resource (if any). The method returns the appropriate CoAP
 84response. Since this library attempts to be OS-independent, the code for
 85retrieving incoming requests and sending responses to these requests
 86depends on your environment. For example, CoAP request may be read from
 87a UDP socket in a POSIX environment.
 88
 89For or a more detailed and complete usage example refer to
 90[zig-riscv-embedded][zig-riscv github] which reads incoming requests
 91from a [SLIP][rfc 1055] serial interface.
 92
 93## Test vectors
 94
 95For parsing code, test vectors are created using the existing
 96[go-coap][go-coap github] implementation written in [Go][go website].
 97Test vectors are generated using `./testvectors/generate.go` and
 98available as `./testvectors/*.bin` files. These files are tracked in the
 99Git repositories and thus Go is not necessarily needed to run existing
100tests.
101
102Each Zig test case embeds this file via [`@embedFile`][zig embedFile].
103All existing Zig parser test cases can be run using:
104
105	$ zig test src/packet.zig
106
107New test cases can be added by modifying `./testvectors/generate.go` and
108`./src/packet.zig`. Afterwards, the test case files need to be
109regenerated using:
110
111	$ cd ./testvectors && go build -trimpath && ./testvectors
112
113New test vectors must be committed to the Git repository.
114
115## License
116
117This program is free software: you can redistribute it and/or modify it
118under the terms of the GNU Affero General Public License as published by
119the Free Software Foundation, either version 3 of the License, or (at
120your option) any later version.
121
122This program is distributed in the hope that it will be useful, but
123WITHOUT ANY WARRANTY; without even the implied warranty of
124MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
125General Public License for more details.
126
127You should have received a copy of the GNU Affero General Public License
128along with this program. If not, see <https://www.gnu.org/licenses/>.
129
130[rfc 7252]: https://datatracker.ietf.org/doc/rfc7252/
131[rfc 7228]: https://datatracker.ietf.org/doc/rfc7228/
132[rfc 1055]: https://datatracker.ietf.org/doc/rfc1055/
133[zig web]: https://ziglang.org/
134[zig-riscv github]: https://github.com/nmeum/zig-riscv-embedded
135[go-coap github]: https://github.com/plgd-dev/go-coap
136[go website]: https://golang.org
137[zig embedFile]: https://ziglang.org/documentation/0.9.1/#embedFile
138[zig import]: https://ziglang.org/documentation/0.9.1/#import
139[git submodules]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
140[gyro github]: https://github.com/mattnite/gyro