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.13.0`.
 17
 18## Usage
 19
 20As noted above, this library targets freestanding constrained devices.
 21For this reason, all memory is statically allocated. To implement a CoAP
 22server with zoap, the central data structure is the Dispatcher. This
 23Dispatcher takes a list of Resources and forwards incoming requests to
 24them if the URI in the request matches one of the available resources.
 25Both, the dispatcher and the resources need to be statically allocated,
 26e.g. as global variables:
 27
 28	const resources = &[_]zoap.Resource{
 29	    .{ .path = "hello", .handler = helloHandler },
 30	    .{ .path = "about", .handler = aboutHandler },
 31	};
 32	var dispatcher = zoap.Dispatcher{
 33	    .resources = resources,
 34	};
 35
 36The code above allocates a dispatcher with two resources: `/hello` and
 37`/about`. An incoming CoAP request for either of those resources invokes
 38the associated handler function. The `helloHandler` implementation may
 39looks as follows:
 40
 41	pub fn helloHandler(resp: *zoap.Response, req: *zoap.Request) codes.Code {
 42	    if (!req.header.code.equal(codes.GET))
 43	        return codes.BAD_METHOD;
 44	
 45	    const w = resp.payloadWriter();
 46	    w.writeAll("Hello, World!") catch {
 47	        return codes.INTERNAL_ERR;
 48	    };
 49	
 50	    return codes.CONTENT;
 51	}
 52
 53The function takes two parameters: The resulting CoAP response and the
 54incoming CoAP request. The handler returns the CoAP response code for
 55the incoming request. The implementation above first checks the request
 56method, if it doesn't match the expected method a response with a
 57Method Not Allowed status code is returned. Otherwise, the
 58`helloHandler` writes `Hello, World!` to the response body and, unless an
 59error occurs, it responses with a successful content response code.
 60
 61In order to invoke these handlers, incoming CoAP requests need to be
 62forwarded to the Dispatcher via the `Dispatcher.dispatch` method which
 63takes an incoming CoAP request as a parameter and forwards it to the
 64matching resource (if any). The method returns the appropriate CoAP
 65response. Since this library attempts to be OS-independent, the code for
 66retrieving incoming requests and sending responses to these requests
 67depends on your environment. For example, CoAP request may be read from
 68a UDP socket in a POSIX environment.
 69
 70For or a more detailed and complete usage example refer to
 71[zig-riscv-embedded][zig-riscv github] which reads incoming requests
 72from a [SLIP][rfc 1055] serial interface.
 73
 74## Test vectors
 75
 76For parsing code, test vectors are created using the existing
 77[go-coap][go-coap github] implementation written in [Go][go website].
 78Test vectors are generated using `./src/testvectors/generate.go` and
 79available as `./src/testvectors/*.bin` files. These files are tracked
 80in the Git repositories and thus Go is not necessarily needed to run
 81existing tests.
 82
 83Each Zig test case embeds this file via [`@embedFile`][zig embedFile].
 84All existing Zig parser test cases can be run using:
 85
 86	$ zig test src/packet.zig
 87
 88New test cases can be added by modifying `./src/testvectors/generate.go` and
 89`./src/packet.zig`. Afterwards, the test case files need to be regenerated
 90using:
 91
 92	$ cd ./src/testvectors && go build -trimpath && ./testvectors
 93
 94New test vectors must be committed to the Git repository.
 95
 96## License
 97
 98This program is free software: you can redistribute it and/or modify it
 99under the terms of the GNU Affero General Public License as published by
100the Free Software Foundation, either version 3 of the License, or (at
101your option) any later version.
102
103This program is distributed in the hope that it will be useful, but
104WITHOUT ANY WARRANTY; without even the implied warranty of
105MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
106General Public License for more details.
107
108You should have received a copy of the GNU Affero General Public License
109along with this program. If not, see <https://www.gnu.org/licenses/>.
110
111[rfc 7252]: https://datatracker.ietf.org/doc/rfc7252/
112[rfc 7228]: https://datatracker.ietf.org/doc/rfc7228/
113[rfc 1055]: https://datatracker.ietf.org/doc/rfc1055/
114[zig web]: https://ziglang.org/
115[zig-riscv github]: https://github.com/nmeum/zig-riscv-embedded
116[go-coap github]: https://github.com/plgd-dev/go-coap
117[go website]: https://golang.org
118[zig embedFile]: https://ziglang.org/documentation/0.9.1/#embedFile
119[zig import]: https://ziglang.org/documentation/0.9.1/#import
120[git submodules]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
121[gyro github]: https://github.com/mattnite/gyro