elm-lang / websocket Goto Github PK
View Code? Open in Web Editor NEWWebsockets for Elm
Home Page: http://package.elm-lang.org/packages/elm-lang/websocket/latest
License: BSD 3-Clause "New" or "Revised" License
Websockets for Elm
Home Page: http://package.elm-lang.org/packages/elm-lang/websocket/latest
License: BSD 3-Clause "New" or "Revised" License
Er sooooo.
Elm 0.19 is out. The elm guide is entirely examples from 0.19. But this package is both broken from the package manager (websocket 1.0.2 just wont install at all), and, even cooler, throws a pretty bracing error at the compiler level if you try to vendor it:
Creating `effect` modules is relatively experimental. There are a couple in @elm
repos right now, but we have decided to be very cautious in expanding its usage.
Get rid of all the effect stuff in WebSocket to proceed.
Note: You can learn the reasoning behind this design choice at
<https://elm-lang.org/0.19.0/effect-modules>
My favorite part about this error is that the link "explaining" it 404s.
So uh. What... what now?
I'd like to send protobuf messages over a websocket, but the only interface that WebSocket exposes is String-based.
In some case we have to send several messages in the socket at once, but in a specific order, which cannot be guaranteed by using several Cmd.
I can see 2 possible APIs for that:
add a specific function:
sendMany : List (String) -> Cmd msg
add a task-based API:
sendTask : String -> Task Error ()
To reproduce this issue, try the SSCCE that follows and double click on the Toggle button a couple of times. You should see multiple open/pending connections in Chrome's Network tab. If you allow some delay between clicks, connections get closed properly.
I have tried this on macOS version 10.12.5 using Chrome version 58.0.3029.110 and Chrome Canary version 61.0.3132.0.
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import WebSocket
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
echoServer : String
echoServer =
"wss://echo.websocket.org"
type alias Model = Bool
init : (Model, Cmd Msg)
init =
(True, Cmd.none)
type Msg
= NewMessage String
| Toggle
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Toggle ->
(not model, Cmd.none)
_ ->
(model, Cmd.none)
subscriptions : Model -> Sub Msg
subscriptions model =
if model
then WebSocket.listen echoServer NewMessage
else Sub.none
view : Model -> Html Msg
view model =
div [] [ button [onClick Toggle] [text "Toggle"] ]
Websockets are stateful conversations. When this library auto-reconnects, it doesn't tell anyone it's done so. The server just thinks it has a new connection unrelated to the previous on-going conversation. The client isn't given a chance to remind the server who it is and, in fact, has no idea that it's having an entirely different conversation as far as the server is concerned. It's a little like invasion of the body snatchers, tbh.
When you attempt to use this WebSocket module with phoenix, the phoenix server stores all the conversation's state, including what channels that have been joined, in the phoenix server's representation of the socket. When a reconnect happens, the new socket has none of that state. From the client's perspective, it's as if it's been dropped from all the channels but with no notification that that's happened. All messages from the server to the client come over channels, so the client will no longer get any messages and not even know that it needs to start waving its arms around wildly to get the server's attention.
I know there is a specific problem with phoenix, but I think the same problem would exist with any websocket approach as, again, it's a stateful conversation we're talking about.
The solution is fairly simple. This library should notify its clients upon reconnect. This will give the client a chance to tell the server who it is and that it needs a full dump of any missing state.
One might be able to cobble something together by sending a periodic "Here I am Identity Heartbeat" type message to the server that would let the server say "Wait! That's you! No way! You're way behind!" However, since that effectively is a poll it would need to be at some interval and during the interval the client might be woefully out of date. Depending on what you're doing, it might be okay. It's obviously not great though and kind of undermines the websocket thing for a lot of applications.
I also don't think the solution is "just use the WebSocket.LowLevel stuff". That's not the solution because the fix is pretty easy and w/o the fix, sadly, I think the WebSocket module's use is fairly limited -- at least limited to applications that don't mind an occasional unrecoverable failure that manifests as...what was I saying. Also, there's a lot of complicated code in WebSocket.elm (thank you!) and it's really close to being quite useful for a wide set of purposes even with a low surface area API.
Elm Version: 0.18
OS: Mac OS X
Browser: Chrome
SSCCE:
Because this is a bug internal to the websocket library, it can't be illustrated in an isolated example. Here are the simplest steps to reproduce:
Websocket.elm
in the elm-stuff/packages/websocket/1.0.2
directory. Add log statements to this line and this line so that you can see when they are fired.Detailed description:
The BadOpen
message is never dispatched, presumably because the Die
message is dispatched with every failed connect attempt. This appears to be happening because the native websocket onClose
handler is fired every failed connect attempt, and then onSelfMsg
handles the Die
message, resetting the connection state.
It appears that the browser itself does some sort of connection throttling, which may be why this issue has gone unnoticed.
When I call WebSocket.listen
from within the outermost parent (Main.elm for example), thing seem to work as expected, however when I call WebSocket.listen
on child components (Component.elm) I get erratic results. In particular the Msg aren't properly tagged and pattern matching in the update
function doesn't work properly..
In the later scenario (WebSocket.listen
called from Component.elm), the first Message Received from the Server is properly returned, however subsequent messages lose the appropriate Msg tagging which screws up pattern matching and passing Component.Msg down...
Here's an example repo with a simple Parent & Child Component and a minimal Websocket Server (in nodejs).
If you download the repo and follow steps in the README.md, you'll see what's happening.
https://github.com/knowthen/elm-websocket-bug-rpt
Not all browsers support websockets - it would be great to know if websockets are actually supported in the browser used, so you can fall back on something else.
As far as I can tell, there is currently no way to check this.
Perhaps a command isSupported or fail an open with a new BadOpen?
Background: I'm writing an app where a websocket message is sent on initial page load requesting data from the server
Issue: calling WebSocket.send
in Http.App.program's init
doesn't work. Message seems to be lost.
init : (Model, Cmd Msg)
init =
-- Looking at chrome dev tools, this string is never sent
(Model "" "", WebSocket.send url "hello first time" )
Speculation: Seems to be either a timing issue waiting on the websocket to connect, or not properly queuing and sending the message after connected, or potentially some sort of runtime problem like this issue: https://github.com/elm-lang/core/issues/582
Calling WebSocket.send
in update
from a UI triggered Msg
does work.
Here's the code: https://gist.github.com/knowthen/9348d4eda390139a82de91dc75167eab
Here's a quick demo showing the initial message isn't sent (on page load)
Then when I click the button, the message is sent and echoed back.
Example javascript code
new WebSocket("ws://localhost:5280/ws-xmpp", ["xmpp"])
While building an Elm app connecting to a Phoenix channel, I was using tokens that contained #
characters in them. As I naively thought they were just a plain string I wasn't uri encoding it before adding it to the query parameters of the socket url.
There is a DOMException
when attempting to do new WebSocket(url)
and it is being swallowed, is there a way to surface this error?
I believe this would solve #10
There are a number of reasons we would want to know when a connection is established or closed:
Hypothetical not-well-vetted API:
type RetryInfo
= NoRetry -- Will not attempt to re-establish the connection
| RetryIn Int -- Will attempt to re-establish the connection in the specified number of seconds
type alias ConnectionClosedHandler msg
= String -- Connection URI
-> Int -- CloseEvent code
-> RetryInfo -- Retry information
-> msg
connectionClosed : ConnectionClosedHandler -> Sub msg
connectionOpened : (String -> msg) -> Sub msg
I wouldn't mind taking a stab at implementing this, assuming I can get some feedback on the proposed API.
Is there a fallback plan for working with browsers that don't support webSocket?
Quite often people ask in the elm slack how to use this package.
It would be nice if the README.md
would explain and help people new to the language that there is currently (as of today with elm 0.19.1) no native websocket support. It might especially misleading that the elm.json
was updated.
It would also help to link to workarounds using ports like https://package.elm-lang.org/packages/bburdette/websocket/latest/
When I try to install the package as well as when trying to download the zip for the release I get a 500 error, I was able to install it a while after this but I still dont know the reason it failed in the first place so it may be something to look into.
Although this is probably just a problem with the repo, im using ubuntu 16.04 and elm 0.18
What about plans to publish elm 0.19.0 package?
Websocket reconnect attempts should be spaced out randomly, to spread out any spikes that might have caused the outage in the first place. Otherwise you'll just overload the server again every 2^c
seconds.
Ethernet uses a random backoff between 0
and 2^c-1
, and we should do something similar.
Is subMap
a dead code, or does it serve eg. as a documentation for people trying to figure out effect managers?
Can it be deleted?
https://github.com/elm-lang/websocket/blob/master/src/WebSocket.elm#L102
I am trying to pass my user credentials to my websocket server using the url ws://username:[email protected]
. Unfortunately on the server side I don't see any Authorization
header.
I initially thought about an old chromium issue but I have the exact same issue on FIrefox.
It would be very nice to support HTTP basic auth during upgrade request or even better, allow us to pass additional headers (maybe in the low level API).
It would be nice to be able to use websocket connections with 0.19
, since I found no way to just use the master branch of a repo in Elm.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.