Skip to content

Commit

Permalink
fix(binding-coap): send error response if content negotiation fails
Browse files Browse the repository at this point in the history
  • Loading branch information
JKRhb committed Oct 19, 2022
1 parent 0e913be commit f53965b
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 10 deletions.
46 changes: 39 additions & 7 deletions packages/binding-coap/src/coap-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,23 +244,52 @@ export default class CoapServer implements ProtocolServer {
}
}

/**
* Negotiates the Content-Format that should be used for a CoAP response.
*
* If a specific Content-Format is requested by a client, as indicated by
* an Accept option, it will be set for the outgoing response if it is supported
* for the resource in question.
* If no Accept option is set, the default Content-Format will be used as a fallback.
* In these cases, the function returns `true` to indicate a successful negotation.
*
* If an Accept option is present but the Content-Format is not supported, the response
* will be sent immediately with a status code `4.06` (Not Acceptable) and an error
* message as a diagnostic payload in accordance with RFC 7252, sections 5.10.4 and
* 5.5.2.
* In this case, the function returns `false` to indicate the failed negotiation.
*
* @param req The incoming request.
* @param res The outgoing response.
* @param availableContentFormats The Content-Formats available for this resource.
* @param defaultContentFormat The default Content-Format used as a fallback.
* @returns `true` if the negotiation did not encounter any error, `false` otherwise.
*/
private negotiateContentFormat(
req: IncomingMessage,
res: OutgoingMessage,
availableContentFormats: string[],
defaultContentFormat: string
) {
): boolean {
const accept = req.headers.Accept;

if (typeof accept === "string" && availableContentFormats.includes(accept)) {
debug(`Received available Content-Format ${accept} in Accept option.`);
res.setHeader("Content-Format", accept);
return;
return true;
}

if (accept == null) {
debug(`Request did not contain an accept option, using the default ${defaultContentFormat}.`);
res.setHeader("Content-Format", defaultContentFormat);
}

debug("Request did not contain an accept option or Content-Format is not supported.");
debug(`Request contained an accept option whose Content-Format ${accept} is not supported.`);

res.setHeader("Content-Format", defaultContentFormat);
res.code = "4.06";
res.end(`Content-Format ${accept} is not supported by this resource.`);

return false;
}

private async handleRequest(req: IncomingMessage, res: OutgoingMessage) {
Expand Down Expand Up @@ -341,14 +370,17 @@ export default class CoapServer implements ProtocolServer {
if (segments.length === 2 || segments[2] === "") {
// Thing root -> send TD
if (req.method === "GET") {
this.negotiateContentFormat(
const negotiationSuccessful = this.negotiateContentFormat(
req,
res,
[ContentSerdes.DEFAULT, ContentSerdes.TD],
ContentSerdes.TD
);
res.code = "2.05";
res.end(JSON.stringify(thing.getThingDescription()));

if (negotiationSuccessful) {
res.code = "2.05";
res.end(JSON.stringify(thing.getThingDescription()));
}
} else {
res.code = "4.05";
res.end("Method Not Allowed");
Expand Down
9 changes: 6 additions & 3 deletions packages/binding-coap/test/coap-server-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import * as TD from "@node-wot/td-tools";
import CoapServer from "../src/coap-server";
import { CoapClient } from "../src/coap";
import { Readable } from "stream";
import { request } from "coap";
import { IncomingMessage, request } from "coap";

// should must be called to augment all variables
should();
Expand Down Expand Up @@ -299,11 +299,14 @@ class CoapServerTest {
for (const contentFormat of contentFormats) {
const req = request(uri);
req.setHeader("Accept", contentFormat);
req.on("response", (res) => {
req.on("response", (res: IncomingMessage) => {
const requestContentFormat = res.headers["Content-Format"];

if (contentFormat === unsupportedContentFormat) {
expect(requestContentFormat).to.equal(defaultContentFormat);
expect(res.code).to.equal("4.06");
expect(res.payload.toString()).to.equal(
`Content-Format ${unsupportedContentFormat} is not supported by this resource.`
);
} else {
expect(requestContentFormat).to.equal(contentFormat);
}
Expand Down

0 comments on commit f53965b

Please sign in to comment.