Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[http] Differentiating sse endpoint #342

Open
egekorkan opened this issue Dec 11, 2023 · 5 comments
Open

[http] Differentiating sse endpoint #342

egekorkan opened this issue Dec 11, 2023 · 5 comments
Labels
http related to http protocol binding

Comments

@egekorkan
Copy link
Contributor

We are not very clear how one would differentiate different forms when there is the same HTTP endpoint but one form has readproperty and the other one has observeproperty. It is not possible to have that in longpolling but it is possible in sse (more on it later). More specifically, we can have the following property:

"properties": {
    "result": {
      "type": "number",
      "readOnly": true,
      "writeOnly": false,
      "observable": true,
      "forms": [
        {
          "href": "properties/result",
          "contentType": "application/json",
          "op": "readproperty"
          ]
        },
        {
          "href": "properties/result",
          "contentType": "text/event-stream", // should it be application/json ?
          "op": "observeproperty",
          "subprotocol": "sse"
        }
      ]
    }

This is technically possible since the Consumer can set the Accept header to "text/event-stream" and the Thing can differentiate based on the header and deliver correct value or stream. In this case, we should probably have the second form looking like the following:

        {
          "href": "properties/result",
          "contentType": "text/event-stream",
          "op": "observeproperty",
          "subprotocol": "sse",
          "htv:methodName": "GET",
          "htv:headers": [
            {
              "@type": "htv:RequestHeader", // not sure about that
              "fieldValue": "text/event-stream",
              "fieldName": "Accept"
            }
          ]
        }

I think that this header should be a default value when there is subprotocol:sse since the EventSource spec says that it is a MAY and not a MUST to send this header (see step 10 at https://html.spec.whatwg.org/multipage/server-sent-events.html#the-eventsource-interface under the green box). Also, it is not clear how this should look like in code. In node-wot, HTTP binding simply uses the event source library at https://github.com/eclipse-thingweb/node-wot/blob/master/packages/binding-http/src/subscription-protocols.ts#L112 . The library however allows headers to be passed at https://github.com/EventSource/eventsource/blob/master/lib/eventsource.js#L35 .

Maybe this is an implicit knowledge (also see https://eclipse.dev/ditto/httpapi-sse.html), but I got into the details for the first time. We should at least document this but more behavior description would be nice in this case.

@egekorkan egekorkan added the http related to http protocol binding label Dec 11, 2023
@lu-zero
Copy link
Contributor

lu-zero commented Jan 25, 2024

I guess more generally we should suggest to always request the content type if more than 1 content type is available for a declared endpoint+verb. We should advise it even more strongly on the subprotocol specifications if they could be multiplexed.

@relu91
Copy link
Member

relu91 commented Jan 31, 2024

I think this question:

  {
          "href": "properties/result",
          "contentType": "text/event-stream", // should it be application/json ?
          "op": "observeproperty",
          "subprotocol": "sse"
        }

It deserves its own issue.

@lu-zero
Copy link
Contributor

lu-zero commented Jan 31, 2024

To be noted:

Otherwise, if res's status is not 200, or if res's [Content-Type](https://html.spec.whatwg.org/multipage/urls-and-fetching.html#content-type) is not [text/event-stream](https://html.spec.whatwg.org/multipage/iana.html#text/event-stream), then fail the connection.

The contentType has to be text/event-stream. So it is implicit in subprotocol:sse and it is an hard discriminant between http-subprotocols.

@benfrancis
Copy link
Member

This is implemented in WebThings Gateway (Producer) and Krellian Cloud (Consumer).

WebThings Gateway always exposes readproperty, writeproperty and observeproperty operations on a given Thing using the same endpoint URL. The server differentiates between requests based on HTTP method and Accept header (i.e. standard HTTP Content Negotiation). The Thing Descriptions expose separate Forms for ["readproperty", "writeproperty"] and ["observeproperty", "unobserveproperty"] operations, but it doesn't actually include a contentType member in either Form because application/json is the default and text/event-stream is implied by the subprotocol member being set to sse.

The same approach can be used to differentiate between endpoints for readallproperties/writeallproperties and observeallproperties/unobserveallproperties. E.g. this example taken directly from the WoT Profiles specification:

  "forms": [
    {
      "op": ["readallproperties", "writemultipleproperties"],
      "href": "properties"
    },
    {
      "op": ["observeallproperties", "unobserveallproperties"],
      "href": "properties",
      "subprotocol": "sse"
    },
    {
      "op": "queryallactions",
      "href": "actions"
    },
    {
      "op": ["subscribeallevents", "unsubscribeallevents"],
      "href": "events",
      "subprotocol": "sse"
    }
  ]

@benfrancis
Copy link
Member

See also: w3c/wot-profile#398

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http related to http protocol binding
Projects
None yet
Development

No branches or pull requests

4 participants