olahol / melody Goto Github PK
View Code? Open in Web Editor NEW:notes: Minimalist websocket framework for Go
License: BSD 2-Clause "Simplified" License
:notes: Minimalist websocket framework for Go
License: BSD 2-Clause "Simplified" License
Sorry, disregard, too many tabs open :(
This pattern is repeated several times in melody_test.go:
conn, err := NewDialer(server.URL)
defer conn.Close()
if err != nil {
t.Error(err)
return false
}
However, if there is an error, this is liable to cause a null reference panic.
Server how to use Gin c *gin.Conext
name := c.PostForm("name")
then calc the result by name
end c.Json(200, gin.H{"name": calcAfter})
to send client
It looks like all broadcasts so far comprise of wrapping a specified byte slice into an envelope
and sending it over the channel *Melody.hub.broadcast
.
For performance reasons, I'm looking to minimize the number of allocations by using sync.Pool
and need to know when a broadcast has been done synchronously.
Would you be open to a PR for this?
I am working with GoFiber. I feel comfortable seeing this WebSocket framework. Can I use this in my GoFiber application?
Please add this:
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
Hi,
We just received the following error in our server:
fatal error: concurrent map read and map write
goroutine 900594 [running]:
runtime.throw(0xff43c6, 0x21)
/usr/local/go/src/runtime/panic.go:1116 +0x72 fp=0xc0050ad860 sp=0xc0050ad830 pc=0x437c52
runtime.mapaccess2_faststr(0xe996c0, 0xc005367290, 0xfd8f40, 0x5, 0x0, 0x0)
/usr/local/go/src/runtime/map_faststr.go:116 +0x4a5 fp=0xc0050ad8d0 sp=0xc0050ad860 pc=0x415de5
gopkg.in/olahol/melody%2ev1.(*Session).Get(...)
/home/erdem/Documents/gopath/code/pkg/mod/gopkg.in/olahol/[email protected]/session.go:201
When we checked it seems like the rwmutex in the session struct is not used to protect the Keys map inside it but only used to set and get the closed state. I couldn't figure out if this is deliberate and the consumer of the package is supposed to protect the Keys access with their own mutex. If it is then maybe a two new access methods can be added like SafeGet and SafeSet that uses the session rw mutex to protect access.
I can submit a pull request if you like for this issue.
Hey,
I need a way to figure out if a there are active sessions because I only want to do work if there are any.
I almost implemented something like the melody.hub
myself, but I think that it would be better to make this public or provide some kind of API.
Thanks
With my recent changes (even before them honestly) I think melody is at a good spot for a tagging a v1 release, which would allow users to use gopkg.in
to do stable imports of the package. What do you think @olahol?
Hi guys,
love your API compared with gorilla/websockets. Also loved that you guys built on top of that solid base instead of doing everything from scratch.
Let me ask: is there a proper way to "deny" the upgrade? I will only allow authenticated users to open a websocket connection:
r.Get("/ws", func(w http.ResponseWriter, r *http.Request) {
var input struct {
Token string `json:"token"`
}
if err := render.Bind(r, &input); err != nil {
http.Error(w, http.StatusText(401), 401)
return
}
...
Again, thanks for the sweet project.
I am looking for some way to pass the Context I already have into a Session context. Currently I don't think there is any way to do this.
Is HandleRequest
returning the Session it creates something that would be worthwhile?
authorized.GET("/ws", func(c *gin.Context) {
session := m.HandleRequest(c.Writer, c.Request)
session.Keys = c.Keys
})
Or possibly injecting Keys directly?
authorized.GET("/ws", func(c *gin.Context) {
m.HandleRequestWithContext(c.Writer, c.Request, c.Keys)
})
I think Melody upgrader variable should be exported
func (s *Session) writeMessage(message *envelope) {
select {
case s.output <- message:
default:
s.melody.errorHandler(s, errors.New("Message buffer full"))
}
}
Here s.output <- message could panic if s.output is already closed, which causes the whole application to exit.
It looks like the problem is:
panic:
[stderr] send on closed channel
[stderr] goroutine 72 [running]:
[stderr] ....melody.(_Session).writeMessage(0xc8203c6f60, 0xc820342000)
.... melody.(_Session).ping
func (s *Session) ping() {
s.writeMessage(&envelope{t: websocket.PingMessage, msg: []byte{}})
}
We should use s.writeRaw instead of s.writeMessage in ping().
what's the usage of the parameter Keys in struct Session
the session.Write func does not return an error and just panics if for example the underlying connection is closed. I think there should be an error returned as well.
Looks like maximum length is 513 symbols. You can just send any 514 symbol long string to verify.
I serve http with gin-gonic, here's the WS part:
mrouter.HandleMessage(func(s *melody.Session, bytes []byte) {
log.Printf("WS message: %s\n", bytes)
Log message is never printed in case of socket close.
Can you add sample to work with graceful shutdown?
like there:
https://github.com/gin-gonic/gin/tree/master/examples/graceful-shutdown/graceful-shutdown
Currently graceful shutdown produces error "context deadline exceeded"
hi,how to receive ping use js client ?
I can't receive big (about 1Kbyte) data with the following error:
websocket: read limit exceeded
How can I configure my Melody to inclease this limit? Thank you for your help!
Hello!
This is a bit cosmetic, but when trying out the basic example I've found that when you close the page (the ws connection) Gin is giving a log message with 200 code routing to /ws, with the amount of time the connection was alive. You can try it out. Is this intended or can be fixed?
It actually also gives a 304 for a root route: https://s.vadimyer.com/1632849b-d353-4882-bee1-72bf006e645c.png
func (s *Session) Set(key string, value interface{}) {
if s.Keys == nil {
s.Keys = make(map[string]interface{})
}
s.Keys[key] = value
}
// Get returns the value for the given key, ie: (value, true).
// If the value does not exists it returns (nil, false)
func (s *Session) Get(key string) (value interface{}, exists bool) {
if s.Keys != nil {
value, exists = s.Keys[key]
}
return
}
I think session must have concurrent safe map.
https://golang.org/pkg/sync/#Map
Or just locks.
examples/filewatch/main.go imports "github.com/go-fsnotify/fsnotify"
. However, this import path is deprecated and deleted. This makes the Go tool error out because the package contains no source code.
Update the import path to "github.com/fsnotify/fsnotify"
.
I sent a token a long with the message to websocket. I want to check if the token valid or not. So how to return error when the server recieve invalid token in HandleMessage function
I couldn't find anywhere in the code a line where *Melody.disconnectHandler
is called.
That function isn't exported, so it is not being called by another package, right?
hi,how to receive ping use js client ?
Hello!
I understand why you prefer to keep melody as much simple as possible. However, I'm not really sure what is the recommended way to implement rooms. May I ask for the example or short tutorial?
I would like to see that broadcasting happens for the related room's clients only, without the need to filter all existing server clients.
My first thought was to call m := melody.New() multiple times, for every room, but this is confusing me:
r.GET("/ws", func(c *gin.Context) {
m.HandleRequest(c.Writer, c.Request)
})
... handler is created only once.
Could you please show how would you implement rooms? Performance among one room's users broadcasting is most important.
Hello! It seems that sending message in its own goroutine:
case m := <-h.broadcast:
for s := range h.sessions {
if m.filter != nil {
if m.filter(s) {
go s.writeMessage(m)
}
} else {
go s.writeMessage(m)
}
}
can theoretically result in wrong message order when message rate is high as code relies on the order of goroutine execution.
Maybe I miss something or it's not an issue for melody – just noticed and decided to ask.
It looks like once you start a melody server, that hub.run()
goroutine will continue forever. This usually makes sense, but in my tests I often want to start up a hub, through some websocket connections at it, then tair it down at the end of the test.
What do you think about adding the ability to call Run
with some sort of cancellation? Maybe either a context or just a chan struct{}
that you would close to stop the run?
Would be useful to access some of the informations regarding the connected clients: for example how many clients are connected currently to the hub, etc.
In the current version there's no way to access these, because there's no method exposing them.
Is something like this planned? I'd be happily take this task.
hi
how i get active sessions for use in private chat?
thanks
I wonder if this is expected behaviour?
Thanks for this great project!
@olahol , or anyone. Did you reach the end of the scope of this project?
hello, How I can return as json instead of servefile. I want to get as json to socket and I will make client with js completely. do you have any example about this?
In a fork of this when it was essentially abandoned someone contributed concurrency safety in a PR.
This also had the session.UnSet() function
// UnSet will delete the key and has no return value
func (s *Session) UnSet(key string) {
s.rwmutex.Lock()
defer s.rwmutex.Unlock()
if s.keys != nil {
_, exists := s.keys[key]
if exists {
delete(s.keys, key)
}
}
}
Would you be willing to add this?
Because if you set e.g. a string to "" it's still set, the value is "" but when it's not in the keys, you get what I mean.
p.s. correction, it was me who added that :D so, no copyright issue there.
Altough this is probably better
// UnSet will delete the key and has no return value
func (s *Session) UnSet(key string) {
s.rwmutex.Lock()
defer s.rwmutex.Unlock()
if s.Keys != nil {
_, exists := s.Keys[key]
if exists {
delete(s.Keys, key)
}
}
}
(Keys is pubic now)
Has support for rooms been considered?
As of now it seems the way to do rooms is to set keys on a Session and use BroadcastFilter to broadcast to a room. Perhaps this is unfounded, but it seems like this could lead to poor performance since every Session is iterated over to check the filter.
Suggestions:
The list of rooms could look like map[string]map[*Session]bool
Thus broadcasting to a room would only have to iterate over those actually in the room.
However, disconnecting would require iterating over all rooms to check for the Session OR each Session would also have to maintain a list of rooms they are in. I think the second option may be favourable if people would be interesting in seeing what room a Session is in easily.
Thoughts?
Hi,
Melody has a ping-pong functionality to keep the connection open and there is a config option PongWait. But how can I detect timeouts? For example, if client's network connection is or becomes too slow - how can I detect it with Melody?
Hi,
Thanks for putting effort into melody. It's the most straight forward Websocket library I've used.
I was hoping to know if melody
maintainers plan to move to nbio
, else or have some other plans.. now that gorilla/websocket
has been archived for about a month.
Things are good at the moment, but some protocol/security issues might come up in recent future. If there is a plan going ahead.
Hi,
Thank you for a great package, it is much cleaner and simpler to use compared to gorilla/websocket library.
I am developing a game server and need to implement an idle timeout, I've set PingPeriod and PongWait values however it doesn't timeout the session or close the connection. In the readme feature list it looks like this is included "Automatic handling of ping/pong and session timeouts" however I can't seem to get it to work. Can you add an example that includes a simple implementation.
Hey, thanks for making this library, it's great!
I was wondering: is the mutex here really necessary? It seems the sessions map will never be written to by more than one goroutine and there would probably be some perf gains by removing the lock.
I'm talk about https://github.com/olahol/melody/blob/master/hub.go#L34-L36 and code below
Hi,
When I want to use the great package melody
to refactor the code that using gorilla/websocket
directly in the project, I have encountered a problem that I can setup any field of the websocket.Upgrader
if it is needed, but in melody
library, when creating a melody instance using melody.New()
, the websocket.Upgrader
is created by default options and there is no exported method to alternate or add other option.
Now, I come up with a idea to bring the functionality to it without changing the code style of the melody
.
The code looks like below. You can also reference the code in the pull request.
Thank you for your time to review it.
options.go
`
type Option func(*websocket.Upgrader)
func WithReadBufferSize(size int) Option {
return func(u *websocket.Upgrader) {
u.ReadBufferSize = size
}
}
func WithWriteBufferSize(size int) Option {
return func(u *websocket.Upgrader) {
u.WriteBufferSize = size
}
}
func WithHandshakeTimeout(d time.Duration) Option {
return func(u *websocket.Upgrader) {
u.HandshakeTimeout = d
}
}
func WithEnableCompression() Option {
return func(u *websocket.Upgrader) {
u.EnableCompression = true
}
}
func WithSubprotocols(protocols []string) Option {
return func(u *websocket.Upgrader) {
u.Subprotocols = protocols
}
}
`
melody.go
`
func New(opts ...Option) *Melody {
upgrader := &websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
for _, opt := range opts {
opt(upgrader)
}
...
}
`
In your code:
func newHub() *hub {
return &hub{
sessions: make(map[*Session]bool),
broadcast: make(chan *envelope),
register: make(chan *Session),
unregister: make(chan *Session),
exit: make(chan *envelope),
open: true,
rwmutex: &sync.RWMutex{},
}
}
But I thank " broadcast: make(chan *envelope, 1024) " is better, or I misunderstand something.
Thanks for your project.
Looks like github.com/labstack/echo has changed significantly since melody was created and the dependency is broken, so build does not work.
func (m *Melody) HandleSentMessageBinary(fn func(*Session, []byte)) {
m.messageSentHandler = fn
}
should be
func (m *Melody) HandleSentMessageBinary(fn func(*Session, []byte)) {
m.messageSentHandlerBinary = fn
}
There seems to be no active maintenance of this repo, but quite a few active forks. Can we transfer ownership of this repo to someone that is currently working on keeping it current?
Thanks!
Hi, I run a melody server on EC2 which uses websockets and I was wondering how I would add SSL support. Is it the same way you would SSL like in a golang http server like so:
http.ListenAndServeTLS(":8081", "cert.pem", "key.pem", nil)
Or do you use something different?
Heya,
I have been looking for something that would provide a clean wrapper around the x/net/websocket or gorilla websocket, but I have not found any good solution yet.
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.