w3c / wot-binding-templates

Web of Things (WoT) Binding Templates
http://w3c.github.io/wot-binding-templates/
Other
22 stars 25 forks source link

Specifying CoAP Response Codes #254

Open JKRhb opened 1 year ago

JKRhb commented 1 year ago

Using the HTTP binding, it is possible to specify a status code in a form with the term htv:statusCodeValue like so:

{
    "href": "/foo",
    "response": {
        "htv:statusCodeValue": 200,
        "contentType": "application/json"
    }
}

For the CoAP binding, a similar vocabulary term is currently missing. CoAP uses a specific encoding for its message codes, in this case three bits to encode the response class (3, 4, or 5) and five bits to encode the detail (00 to 31). The CoAP codes are documented in the specifications and the IANA registry using the format c.dd, e.g. 2.05 for the response code "Content". Therefore, I think there are a few options for specifying the response code in a TD form.

First, we could use a string-based value corresponding to the c.dd format, like so:

{
    "href": "/foo",
    "response": {
        "cov:responseCode": "2.05",
        "contentType": "application/cbor"
    }
}

This approach would be flexible for future additions, but creates some overhead for converting the string into the numerical representation.

A second possible approach would use two integers for expressing the response class and detail, respectively:

{
    "href": "/foo",
    "response": {
        "cov:responseCode": {
            "cov:responseClass": 2,
            "cov:responseDetail": 5
        },
        "contentType": "application/cbor"
    }
}

Here, deriving the response code is a little bit easier, I would say, since you only need to bit-shift once and perform a bitwise OR operation. However, this approach creates more overhead payload-wise, makes the form a bit more difficult to read (for humans), and is not as semantically clear. Therefore, I am leaning a bit more towards the first approach, but I am not fully decided yet.

What would be your preference? And do you see additional alternatives?

ektrah commented 1 year ago

Could you explain the purpose of htv:statusCodeValue in more detail? It's nowhere mentioned the TD specification or the HTTP protocol binding.

HTTP in RDF defines it as "The actual status code value sent by the server (Literal)." That's useful when representing an actual sent response in RDF, but I'm not sure what a client should do with this information when it's in a TD.

In general, I believe a form in a TD should describe the server's capabilities and expected parameters so that the client can successfully perform the operation, not contain an RDF serialization of the messages that are exchanged in the process.

JKRhb commented 1 year ago

Thank you for your feedback, @ektrah! I stumbled upon the htv:statusCodeValue in the context of the Thing Model for the Thing Description Directory API (in section 7.3.2.4) and therefore wondered if an equivalent should also be specified for CoAP. You are right that a consumer would not actually need the vocabulary term, but maybe it could be useful to describe the API provided by a Thing? If not, maybe the use of the htv:statusCodeValue also requires some more discussion.

JKRhb commented 1 year ago

I think this issue will become relevant for the next version of the Discovery specification which is supposed to include a CoAP API for the Thing Description Directory (see here).

ektrah commented 1 year ago

I see. So, the target audience for htv:statusCodeValue is not the client or the server but actually the developer who implements the API on the server. And the intention is to give the developer an API specification that tells them what HTTP status code and payload format to return in which case.

Let's take a concrete example:

        "retrieveThing": {
            "description": "Retrieve a Thing Description",
            ...
            "forms": [
                {
                    "href": "/things/{id}",
                    "htv:methodName": "GET",
                    "response": {
                        "description": "Success response",
                        "htv:statusCodeValue": 200,
                        "contentType": "application/td+json"
                    },
                    "additionalResponses": [
                        {
                            "description": "TD with the given id not found",
                            "contentType": "application/problem+json",
                            "htv:statusCodeValue": 404
                        }
                    ]
                }
            ]
        },

This tells the developer to implement an action that allows a client to retrieve a TD using a GET request to /things/{id}. In the success case, the developer should let the action return a 200 (OK) response with content type "application/td+json". In case the TD with the given ID was not found, the developer should let the action return a 404 (Not Found) response with content type "application/problem+json". Right?

From the client's point of view none of this matters. The client recognizes success or failure by the status code, and the reason for failure is specified in the Problem Details payload. If a client receives a 404 (Not found) response with content type "application/problem+json", it does not necessarily mean that a TD was not found; it could also simply be that the server no longer offers the API. So the conclusion is one-way only: If condition is met, then send status code and content type. But not: If status code and content type received, then condition is met.

If you do this consistently, you accumulate a whole series of error conditions that have to be repeated for each property, action and event: If the client is not authorized to retrieve the TD, then 403 Forbidden. If the TD cannot be delivered in the format requested by the client, then 406 Not Acceptable. If the client made too many TD retrieval requests too fast, then 429 Too Many Requests. And so on.

The problem is that here the HTTP API is described as an RPC interface. It would be better to describe it in a RESTful way: Specify that for the TD with ID {id} there is an HTTP resource at /things/{id}. Boom. Done.

In the success case, the GET request results in a 200 (OK) response with the representation of the resource. If the resource cannot be found, then 404 Not Found. If the client is not authorized to retrieve the resource representation, then 403 Forbidden. If the resource representation cannot be provided in the format requested by the client, then 406 Not Acceptable. This is already defined exactly like that by HTTP and it doesn't have to be repeated over and over again for every property, action and event.

The same applies to CoAP.

JKRhb commented 7 months ago

It's a bit late, but thank you for your detailed response, @ektrah! I think you have explained very well that the response and additionalResponses fields address a different use case that does not have to be covered by "regular" TD which should also be taken into account by the current use case discussion.

While the TD Directory API specification illustrates that there is in fact a use case for "server-side" vocabulary (e.g., HTTP status codes), I think one shortcoming of the current TM specification is that you always need to take over this information into TDs instantiated by the respective TM, even though – as you've shown – it is not needed by a consumer.[^1]

[^1]: In the Scripting API, for example, both fields are not used at all (although response is mentioned in this example).

So I think we would need, for instance, a way for TMs to label fields at the affordance level as optional or state that they should be excluded during the instantiation process. Furthermore, I think we could even consider moving response and additionalResponses out of the core vocabulary into extensions, as this concept probably does not make that much sense for a protocol like MQTT, for example.

JKRhb commented 3 weeks ago

I've just opened https://github.com/w3c/wot-thing-description/issues/2042 to discuss adding the possibility to make more fields optional in TMs (i.e., allowing for not taking them over into the resulting TD). This way, the implementor of a TM could choose to simply omit information like status codes if it is not necessary for the consumer to know. Do you think that with such a mechanism in place, it would make sense to add CoAP response codes to the CoAP Binding Template as well? Or could that even be considered a separate/additional vocabulary to the one we are currently defining?

Note that there is now a discussion on how to handle response and additionalResponses in the WoT Scripting API taskforce, so the general assumption is that a Consumer needs to take that information into account when interacting with a Thing. It seems to me as if we might also need to have a more general discussion about these two fields at some point in the TD task force.