SatCat5
coap_resource.cc
1 // Copyright 2024-2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <cstring>
7 #include <satcat5/coap_reader.h>
9 #include <satcat5/coap_writer.h>
10 #include <satcat5/log.h>
11 
16 namespace log = satcat5::log;
17 
18 Resource::Resource(ResourceServer* server, const char* uri_path)
19  : m_uri_path(normalize_uri(uri_path))
20  , m_server(server)
21  , m_next(nullptr)
22 {
23  if (m_server) m_server->add_resource(this);
24 }
25 
26 #if SATCAT5_ALLOW_DELETION
28  if (m_server) m_server->remove_resource(this);
29 }
30 #endif
31 
32 bool Resource::operator==(const Resource& other) const {
33  // Uri-Path must match, be null-terminated, and less than max length
34  // Some of this validation should happen when Uri-Path is first provided,
35  // set m_uri_path during the add_resource() call in the Server? Would also
36  // allow to drop leading '/', which is a common convention
37  return strncmp(m_uri_path, other.m_uri_path,
38  SATCAT5_COAP_MAX_URI_PATH_LEN) == 0 &&
39  memchr(m_uri_path, '\0', SATCAT5_COAP_MAX_URI_PATH_LEN + 1) &&
40  memchr(other.m_uri_path, '\0', SATCAT5_COAP_MAX_URI_PATH_LEN + 1);
41 }
42 
44  { return m_server->ip(); }
46  { return m_server->udp(); }
47 
49  // Return 2.05 Content with the payload copied
50  Writer reply(obj->open_response());
51  if (!reply.ready()) { return false; }
52  bool ok = reply.write_header(CODE_CONTENT, obj);
53  if (ok && msg->format()) {
54  u16 format = msg->format().value_or(FORMAT_BYTES);
55  ok = reply.write_option(OPTION_FORMAT, format);
56  }
57  satcat5::io::Readable* src = msg->read_data();
58  satcat5::io::Writeable* dst = reply.write_data();
59  if (!ok || !src || !dst) { return false; }
60  return src->copy_and_finalize(dst, io::CopyMode::ALWAYS);
61 }
62 
64  // Validate Content-Format
65  if (msg->format() && msg->format().value() != FORMAT_TEXT) {
66  return obj->error_response(CODE_BAD_FORMAT);
67  }
68 
69  // Write a log entry with a useful prefix, source IP would be better?
71  satcat5::io::Readable* src = nullptr;
72  if (!(src = msg->read_data()) || !src->get_read_ready()) {
73  return obj->error_response(CODE_BAD_REQUEST, "Missing message");
74  }
75  src->copy_to(&log_str);
76  log_str.write_u8(0); // Ensure null termination
78  .write((const char*) log_str.buffer());
79 
80  // Return 2.01 Created
81  Writer reply(obj->open_response());
82  if (!reply.ready()) { return false; }
83  bool ok = reply.write_header(CODE_CREATED, obj);
84  return ok && reply.write_finalize();
85 }
86 
88  // Look up the parsed Uri-Path against the server's registered resources
89  ResourceNull target_resource(msg->uri_path().value_or("")); // Empty is valid
90  Resource* matched_resource = nullptr;
91  Resource* resource = m_resources.head();
92  while (resource) {
93  if (*resource == target_resource) {
94  matched_resource = resource; break;
95  }
96  resource = m_resources.next(resource);
97  }
98 
99  // Return an error if no resources matched
100  if (!matched_resource) {
101  obj->error_response(CODE_NOT_FOUND, "Unrecognized Uri-Path");
102  return;
103  }
104 
105  // The found resource may generate a response, sent as a piggybacked reply
106  bool ok;
107  if (msg->code() == CODE_GET) {
108  ok = matched_resource->request_get(obj, msg);
109  } else if (msg->code() == CODE_PUT) {
110  ok = matched_resource->request_put(obj, msg);
111  } else if (msg->code() == CODE_POST) {
112  ok = matched_resource->request_post(obj, msg);
113  } else if (msg->code() == CODE_DELETE) {
114  ok = matched_resource->request_delete(obj, msg);
115  } else { // Reject per Section 5.8
116  ok = obj->error_response(CODE_BAD_METHOD);
117  }
118 
119  // Send 5.00 SERVER_ERROR if the Resource failed to generate a response
120  if (!ok) {
121  obj->error_response(CODE_SERVER_ERROR); // GCOVR_EXCL_LINE
122  }
123 }
CoAP request/response handling for a single client-server connection.
bool error_response(satcat5::coap::Code code, const char *why=0)
If able, return an error in response to an incoming request from a remote client.
satcat5::io::Writeable * open_response()
If able, accept an incoming request from a remote client.
satcat5::ip::Dispatch * ip() const
Pointer to the parent's network interface.
satcat5::udp::Dispatch * udp() const
Pointer to the parent's network interface.
Code code() const
< Response code (CODE)
Definition: coap_reader.h:114
satcat5::io::Readable * read_data()
Access the message payload.
Definition: coap_reader.cc:90
Base-class for parsing CoAP message headers and options.
Definition: coap_reader.h:162
satcat5::util::optional< const char * > uri_path() const
Accessors for parsed options.
Definition: coap_reader.h:174
satcat5::util::optional< u16 > format() const
Accessors for parsed options.
Definition: coap_reader.h:177
Resource that echos back any incoming payload.
bool request_get(Connection *obj, Reader *msg) override
GET requests respond with the payload from the request.
Define a single CoAP resource.
Definition: coap_resource.h:47
virtual bool request_delete(Connection *obj, Reader *msg)
Event handlers for GET, POST, PUT, and DELETE queries.
Definition: coap_resource.h:95
virtual bool request_put(Connection *obj, Reader *msg)
Event handlers for GET, POST, PUT, and DELETE queries.
Definition: coap_resource.h:93
const char *const m_uri_path
URI-Path for this resource.
bool operator==(const Resource &other) const
Comparison operators for comparing two URI-paths.
virtual bool request_post(Connection *obj, Reader *msg)
Event handlers for GET, POST, PUT, and DELETE queries.
Definition: coap_resource.h:91
satcat5::ip::Dispatch * ip() const
Pointer to the parent's network interface.
~Resource() SATCAT5_OPTIONAL_DTOR
Destructor is only accessible to the child object.
virtual bool request_get(Connection *obj, Reader *msg)
Event handlers for GET, POST, PUT, and DELETE queries.
Definition: coap_resource.h:89
ResourceServer *const m_server
Optional pointer to the server object.
satcat5::udp::Dispatch * udp() const
Pointer to the parent's network interface.
Resource that creates a log::Log entry.
s8 m_priority
Priority for created log messages.
bool request_post(Connection *obj, Reader *msg) override
POST requests create a log::Log entry.
The NullResource does not implement GET, POST, PUT, or DELETE.
Manager for several Resource objects available on an Endpoint.
void coap_request(Connection *obj, Reader *msg) override
Handler for an incoming request.
void remove_resource(Resource *resource)
Remove a Resource from the linked list.
satcat5::util::List< Resource > m_resources
List of resources.
void add_resource(Resource *resource)
Add a Resource to the linked list.
Message formatting for the Constrained Applications Protocol (CoAP).
Definition: coap_writer.h:26
bool ready() const
Is this object ready for writing? Note: This is the only safe method if m_dst is null.
Definition: coap_writer.h:37
bool write_finalize()
After the last option, finish with an empty message.
Definition: coap_writer.h:67
bool write_option(u16 id, unsigned len, const void *data)
Write option(s) one at a time, in various formats.
Definition: coap_writer.cc:49
satcat5::io::Writeable * write_data()
After the last option, start writing message data.
Definition: coap_writer.cc:88
bool write_header(u8 type, Code code, u16 msg_id, u64 token=0, u8 tkl=0)
Always start by writing the header, with optional token.
Definition: coap_writer.cc:25
const u8 * buffer() const
Read-only access to the working buffer.
Definition: io_writeable.h:147
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
bool copy_and_finalize(satcat5::io::Writeable *dst, satcat5::io::CopyMode mode=CopyMode::PACKET)
Copy data to a Writeable object, then finalize.
Definition: io_readable.cc:234
unsigned copy_to(satcat5::io::Writeable *dst)
Copy data to a Writeable object, without finalizing.
Definition: io_readable.cc:214
virtual unsigned get_read_ready() const =0
How many bytes can be read without blocking?
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
Protocol handler and dispatch unit for Internet Protocol v4 (IPv4).
Definition: ip_dispatch.h:43
The Log class creates and formats one log message.
Definition: log.h:195
Log & write(const char *str)
Formatting methods for various data types.
Definition: log.cc:198
Dispatcher sorts incoming UDP messages by port index.
Definition: udp_dispatch.h:20
constexpr Code CODE_BAD_FORMAT(4, 15)
< 4.13 Request Entity Too Large
constexpr Code CODE_BAD_REQUEST(4, 0)
< 2.31 Continue
constexpr u16 FORMAT_TEXT
text/plain;charset=utf-8
constexpr Code CODE_PUT(0, 3)
< 0.02 POST request
constexpr Code CODE_POST(0, 2)
< 0.01 GET request
constexpr Code CODE_DELETE(0, 4)
< 0.03 PUT request
constexpr Code CODE_BAD_METHOD(4, 5)
< 4.04 Not Found
constexpr Code CODE_NOT_FOUND(4, 4)
< 4.03 Forbidden
constexpr Code CODE_GET(0, 1)
< 0.00 Empty message
constexpr u16 FORMAT_BYTES
application/octet-stream
constexpr Code CODE_SERVER_ERROR(5, 0)
< 4.15 Unsupported Content-Format
constexpr u16 OPTION_FORMAT
Option: Content-Format.
constexpr Code CODE_CONTENT(2, 5)
< 2.04 Changed
Message parsing for the Constrained Applications Protocol (CoAP)
CoAP resource handler definition and resource server creation.
Diagnostic logging to UART and/or Ethernet ports.
T value() const
Fetch the inner value.
Definition: utils.h:64
T value_or(const T &t) const
Fetch the inner value if present, or the specified default.
Definition: utils.h:66