w3c / ServiceWorker

Service Workers
https://w3c.github.io/ServiceWorker/
Other
3.63k stars 313 forks source link

Should EventSource and WebSocket be exposed in service workers? #947

Closed annevk closed 6 years ago

annevk commented 8 years ago

It seems like XMLHttpRequest we best treat these as "legacy APIs" that you can use fetch() instead for (at least, once we have upload streams).

Also with the lifetime of a service worker it's unlikely these APIs are useful.

ghost commented 6 years ago

@kentonv we got web sockets in the Cloudflare workers yet?

kentonv commented 6 years ago

@chick-fil-a-21 Currently CF Workers supports WebSocket pass-through -- that is, if all you do is rewrite requests, e.g. changing some headers or the URL, it will "just work" with a WebSocket request. We don't yet support examining or creating WebSocket content, only proxying.

kentonv commented 6 years ago

@unicomp21 Thanks! But this issue thread probably isn't the right place to discuss Cloudflare stuff. I suggest posting a thread on the Cloudflare Community; you can @ me there if you like. You can enable the Workers beta in your Cloudflare account settings (it's one of the boxes along the top).

subversivo58 commented 6 years ago

Hello everyone, I had already moved on to a quick reading on this issue some time ago.

Today testing the APIs available on the global object of a ServiceWorker I was surprised to find WebSocket running on Chrome.

I remember just coming to this discussion some time ago because trying to instantiate a socket in ServiceWorker would return an error.

Soon I ran to see if there were any changes that I had not noticed but it seems to me that there were no changes.

I searched for some information before I decided to share here, if there were any notes about Chrome | Chromium or even bugs reported but I did not find anything.

So I do not really know if there were changes or if this could be an experimental feature or a bug.


PS:

I forgot to mention but, after ServiceWorker gets control of the same page on "refresh" the socket connection is not terminated. This was my test:

// sw.js
const WebSocketConnect = () => {
    let ws = new WebSocket("ws://localhost/signaling", ['token', 'xxxxxxxxx'])
    ws.onopen = function() {
        setInterval(() => {
            if ( 'readyState' in ws && ws.readyState === 1 ) {
                ws.send('Hay')
            }
        }, 5000)
    }

    ws.onmessage = function(e) {
        console.log('Message:', e.data)
    }

    ws.onclose = function(CloseEvent) {
        ws.close()
        console.log(`Socket is clossed, code: ${CloseEvent.code} - reason: ${CloseEvent.reason}`)
        setTimeout(() => {
            WebSocketConnect()
        }, 3000)
    }

    ws.onerror = function(err) {
        console.log(err)
        //console.error('Socket encountered error: ', err.message, 'Closing socket')
        ws.close()
    }
}

WebSocketConnect()
// main.js (Node)
const http = require('http')
const PORT = process.env.PORT || 3000
const WebSocketServer = require('uws').Server
// HTTP Server
const server = http.createServer((req, res) => {
   // usually serve files by routes don't route "/signaling"
})
server.listen(PORT)

// Socket
const wss = new WebSocketServer({
    server: server,
    path: '/signaling'
})

/*
 incremental value for test if ServiceWorker controler page close
 socket connection after page refresh
*/
let increment = 1

// message from ServiceWorker is ignored
const sendMessage = (message, websocket) => {
    increment++
    websocket.send(`increment message - ${increment}`)
}

wss.on('connection', function(ws) {
    ws.on('message', function(message) {
        sendMessage(message, ws)
    })
    // await ServiceWorker close connection after page refresh
    ws.on('close', function(code) {
        console.log(`Socket has been closed. Code: ${code}`) // expect 1001
    })
    // first message from connection
    ws.send('something')
})

The "onclose" event on the server does not receive an advertisement when the ServiceWorker is active and a "simple refresh" is done on the page, nor does the ServiceWorker report.

Using DevTools (I know this has nothing to do with the specification) throting for offline does not enter the socket and neither does the socket connection shown in the Network > WS

kael commented 6 years ago

Seems the reactivation of WebSocket in ServiceWorker started there for Chrome.

Can you clarify a bit what's expected and what's not happening ?

Also, what happens when the WebSocket server uses its native ping ?

subversivo58 commented 6 years ago

Hi @kael

I followed the threaded thread but, I did not find exactly where in that version (path, release) Chrome came to support WebSocket within ServiceWorker. There are some versions back I could not stand.

I hoped that:

This is not happening and the connection continues to be persisted. There may be some scenario that this is helpful, but I believe this is not the standard.

Just out of curiosity: the socket connection started in the ServiceWorker is not shown in the Network > WS. As in Developer Tools, the socket does not lose the connection even after simulating the offline state.

I'm not using the ws module, I'm using the native API in frontend and uws in the backend.


Edit: the socket connection is only shown on the Network > WS tab after the server explicitly shut down the socket (or fall)

On Firefex 61.0.1 when the user presses F5 or when window.location.reload() is used or even when there is a navigation to another page, the socket (frontend) terminates the connection.

I do not know exactly what the correct default behavior is, what the specification defines in these cases (if it defines) but, I believe what happens in Firefox is correct, is not it?

ricea commented 6 years ago

As far as I know, Chrome has supported WebSockets in ServiceWorkers for as long as it has supported ServiceWorkers.

On Firefex 61.0.1 when the user presses F5 or when window.location.reload() is used or even when there is a navigation to another page, the socket (frontend) terminates the connection.

This seems odd, since a single ServiceWorker can have multiple pages as clients. Does navigating any of the pages terminate the connection, or is one specific page special in this regard?

subversivo58 commented 6 years ago

As far as I know, Chrome has supported WebSockets in ServiceWorkers for as long as it has supported ServiceWorkers.

I do not know this, there are a few previous versions (I can not remember for sure) tried to use WebSocket in the ServiceWorker script in Chrome and received an error.

I have two pages served by a ServiceWorker (localhost): index.html and about.html

Firefox opens the WebSocket connection and maintains it for only 30 seconds (+/-) ... before reaching this limit (?) if the user presses the F5 key or if window.location.reload() is released page refreshes and there is no connection loss, it is as if the count would recommence. If you reach/pass this limit, the connection is closed silently without sending a "close frame" ... some time later the server reports 1006 (CLOSE_ABNORMAL).

In Firefox navigate between cached pages (within this "30-second" limit) it also produces the same ... the open connection on the previous page is closed silently without sending a "close frame".

In Chrome there is no time limit and browsing between different pages already cached does not end the connection.

I'm not sure which of the two implementations follows the pattern defined by the WebSocket API (Chrome or Firefox) ... I tried to find reference in SPEC but I think this does not concern WebSocket's spec but rather that of ServiceWorker.

I believe Firefox is closer to the ServiceWorker specification

Note: ServiceWorkerGlobalScope object provides generic, event-driven, time-limited script execution contexts that run at an origin. Once successfully registered, a service worker is started, kept alive and killed by their relationship to events, not service worker clients. Any type of synchronous requests must not be initiated inside of a service worker.

SPEC Reference

Although I do not know if this is intentional since it is not sent a "close frame".

I found interesting persistence in the connection obtained in Chrome but, after all, which of these two distinct implementations is correct (or about to be correct)?

ricea commented 6 years ago

I do not know this, there are a few previous versions (I can not remember for sure) tried to use WebSocket in the ServiceWorker script in Chrome and received an error.

I know of one version where it was broken due to a bug. It wasn't intentional.

Firefox opens the WebSocket connection and maintains it for only 30 seconds

It sounds like they're trying to avoid people assuming that they can make long-term connections from a ServiceWorker. But having a different lifetime for the WebSocket and the variable that references it is really confusing, so I don't consider this a good idea.

If you reach/pass this limit, the connection is closed silently without sending a "close frame" ... some time later the server reports 1006 (CLOSE_ABNORMAL).

This sounds like a bug, since it's a straightforward RFC6455 violation. See https://tools.ietf.org/html/rfc6455#page-44.

subversivo58 commented 6 years ago

@ricea ok

This sounds like a bug, since it's a straightforward RFC6455 violation. See https://tools.ietf.org/html/rfc6455#page-44.

But this (the closing without sending a "close frame") would be the implementation of WebSockets that Firefox does in ServiceWorker or, this would originate from the ServiceWorker routine?

I confess to having been confused ... I must assume that no implementation carried out by the mentioned browsers should be followed/used?

annevk commented 6 years ago

Closing this since this can no longer be "fixed" now it's been shipping all over.

wanderview commented 6 years ago

Firefox opens the WebSocket connection and maintains it for only 30 seconds (+/-) ... before reaching this limit (?) if the user presses the F5 key or if window.location.reload() is released page refreshes and there is no connection loss, it is as if the count would recommence. If you reach/pass this limit, the connection is closed silently without sending a "close frame" ... some time later the server reports 1006 (CLOSE_ABNORMAL).

In Firefox navigate between cached pages (within this "30-second" limit) it also produces the same ... the open connection on the previous page is closed silently without sending a "close frame".

I believe this is firefox implementing the idle timeout of the service worker. If there is no functional event waitUntil() holding the service worker alive it will be stopped to conserve system resources. The idle timeout is restarted when another functional event (like FetchEvent) is dispatched to the service worker.

In Chrome there is no time limit and browsing between different pages already cached does not end the connection.

Do you have devtools open while running this test? At least at one point chrome did not timeout service workers while devtools were open. Not sure if that is still the case or not.

mohammad-masud commented 5 years ago

these are informative for my website called keyboards