igm / sockjs-go Goto Github PK
View Code? Open in Web Editor NEWWebSocket emulation - Go server library
License: BSD 3-Clause "New" or "Revised" License
WebSocket emulation - Go server library
License: BSD 3-Clause "New" or "Revised" License
For some reason sockjs.Options.JSessionID or Options.cookie is never gets called. From what I gather the client never sends OPTIONS /options, so it never really starts. The GET version of option is called however.
To replicate this situation just run: https://github.com/igm/sockjs-go/blob/master/testserver/server.go and run sockjs-client (https://github.com/sockjs/sockjs-client) with url: /cookie_needed_echo with no extra options. It should set a JSessionID cookie with value dummy.
Any pointers would be very welcome :)
Creating a sockjs handler with a prefix that involves a regular expression group breaks parseSessionID
because it uses constant match offsets.
It feels like there are two options:
url.Path
before attempting the session match.In my situation, I have two prefix paths that I want to be treated equivalently. Let's call them:
/foo/bar
/foo
I am attempting to adjust the client, but that's a little out of my control; I originally tried to work around this situation with:
sockjs.NewHandler(
"/foo(/bar)?",
...
)
Hi,
First, THX for your sockjs-go server!
We had one problem with the code producing panics because of writes on already closed channels. We found an already available fix in the couchbaselabs fork of your repo (thx @dustin!)
I've fixed the patch up so it applies to current master:
gebi@f168ef5
original patch:
couchbaselabs@a605190
How do I check if the session is active, and if it's impossible what is the workaround for this?
The EventSource frame writer does no escaping of its messages:
sockjs-go/sockjs/eventsource.go
Line 31 in f490575
Compare that to how sockjs-node handles its EventSource payload:
https://github.com/sockjs/sockjs-node/blob/9efed2c754226b702eb468ef0643b316b91bf37f/lib/transport/eventsource.js#L13-L17
This has caused a problem in our update from sockjs-client-0.3.4 to a more modern sockjs-1.5.x.
In 0.3.4, the EventStream client code decoded the data with unescape
(which does not err on unescaped %
symbols).
https://github.com/sockjs/sockjs-client/blob/0c70698bddcfab826c7b241ed709f69b5b0d41f7/lib/trans-receiver-eventsource.js#L12-L15
In sockjs-1.5.x, data is decoded with decodeURI
, which errs on unescaped %
symbols.
https://github.com/sockjs/sockjs-client/blob/71876b30849299255f34131af6474d959d61cbb2/lib/transport/receiver/eventsource.js#L19-L22
unescape('%')
# => '%'
decodeURI('%')
# => URI error
This problem can be seen using the webecho
example when modified so it only uses the eventsource
or iframe-eventsource
protocol.
// Update the SockJS initialization in
// https://github.com/igm/sockjs-go/blob/master/examples/webecho/web/app.js
// No other modifications are necessary.
// options usage example
var options = {
debug: true,
devel: true,
transports: ["eventsource"]
};
Visit the echo app and send the %
character. This causes an error in the JS console after the client receives the message:
"a[\"%\"]"
The message should be:
"a[\"%25\""
This problem exists both in v2 and v3.
I couldn't find anything about the license for your sockjs implementation. Can you please put one in the repo?
//here is my options
opts := sockjs.DefaultOptions
opts.ResponseLimit = 4096
opts.RawWebsocket = true
opts.CheckOrigin = func(r *http.Request) bool {
fmt.Printf("check origin request %v\n", r.URL)
return true
}
opts.Origin = "*"
opts.WebsocketUpgrader = &websocket.Upgrader{}
opts.WebsocketUpgrader.CheckOrigin = func(r *http.Request) bool { return true }
wsHandler := &WSockHandler{prefix, sockjs.NewHandler(prefix, opts, handler)}
I was wondering whether you'd accept a pull request for some sort of context support in the socket session. Or at least some way of being notified of the session's end.
My use case is that sometimes an incoming websocket message kicks off a long running process. If the socket session dies, I'd like to cancel that process. I tried to pass session.Request().Context() down the line but now realise this doesn't work in fallback modes as session.Request() is only the first request.
I hope I have explained myself clearly enough!
i see there are two month after last commit. and there are sitll some error on tests.
Hello @igm !
Noticed that recently you did migration to v3. I believe that it was rather painful experience due to how go.mod works with dependencies > v2, glad you managed to do it pushing this library to an actual stack 👍
Just tried to introduce v3 dependency:
$ go mod init test_sockjs
go: creating new go.mod: module test_sockjs
$ go get github.com/igm/sockjs-go/v3/sockjs
go: downloading github.com/igm/sockjs-go v2.0.1+incompatible
go: downloading github.com/igm/sockjs-go/v3 v3.0.0-20200426155823-50a65008b72f
go: found github.com/igm/sockjs-go/v3/sockjs in github.com/igm/sockjs-go/v3 v3.0.0-20200426155823-50a65008b72f
$ cat go.mod
module test_sockjs
go 1.14
require github.com/igm/sockjs-go/v3 v3.0.0-20200426155823-50a65008b72f // indirect
I suppose v3.0.0
tag for v3 required so go.mod can use dependency without pointing to exact commit.
Hello,
I met a data race issue when I used the master branch in my code.
Related code in file: session.go
func (s *session) GetSessionState() sessionState { return s.state }
Could you please add s.Lock()/s.Unlock for this method? Or change the sync.Mutex to sync.RWMutex, and something like follow:
func (s *session) GetSessionState() sessionState {
s.RLock()
defer s.RUnlock()
return s.state
}
Thanks a lot.
Hello! First of all thanks for a great work on implementing SockJS server in Go.
I looked through code but have not found raw WebSocket endpoint - i.e. something like ws://localhost:8000/echo/websocket
to use SockJS server without SockJS client.
Is this not implemented?
The GetSessionState
method was added to sockjs.Session
as of #36. This changed the interface on the stable v2
tag.
Currently the buffer size is 1024 and fixed at https://github.com/igm/sockjs-go/blob/master/sockjs/websocket.go#L17 I've just had a problem because one of my messages were longer than 1023 characters.
Should we fix that or I'm doing something wrong?
xhr:30 tries to access the map without any locks so it seems we have to protect it with sessionMux.
I could make a PR.
==================
WARNING: DATA RACE
Write by goroutine 51:
runtime.mapdelete()
/usr/lib/go/src/pkg/runtime/hashmap.c:1152 +0x0
gopkg.in/igm/sockjs-go.v2/sockjs.func·001()
/home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:115 +0x113
Previous read by goroutine 49:
runtime.mapaccess2_faststr()
/usr/lib/go/src/pkg/runtime/hashmap_fast.c:129 +0x0
gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).xhrSend()
/home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/xhr.go:36 +0x459
gopkg.in/igm/sockjs-go.v2/sockjs.*handler.(gopkg.in/igm/sockjs-go.v2/sockjs.xhrSend)·fm()
/home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:42 +0x51
gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).ServeHTTP()
/home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:73 +0x1be
net/http.serverHandler.ServeHTTP()
/usr/lib/go/src/pkg/net/http/server.go:1597 +0x1ca
net/http.(*conn).serve()
/usr/lib/go/src/pkg/net/http/server.go:1167 +0xc00
Goroutine 51 (running) created at:
gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).sessionByRequest()
/home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:117 +0x4d6
gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).xhrPoll()
/home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/xhr.go:53 +0x8b
gopkg.in/igm/sockjs-go.v2/sockjs.*handler.(gopkg.in/igm/sockjs-go.v2/sockjs.xhrPoll)·fm()
/home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:44 +0x51
gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).ServeHTTP()
/home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:73 +0x1be
net/http.serverHandler.ServeHTTP()
/usr/lib/go/src/pkg/net/http/server.go:1597 +0x1ca
net/http.(*conn).serve()
/usr/lib/go/src/pkg/net/http/server.go:1167 +0xc00
Goroutine 49 (running) created at:
net/http.(*Server).Serve()
/usr/lib/go/src/pkg/net/http/server.go:1644 +0x2c1
net/http.Serve()
/usr/lib/go/src/pkg/net/http/server.go:1561 +0xac
github.com/backplane/platform/clientcomm.func·003()
https://github.com/sockjs/sockjs-client#connecting-to-sockjs-without-the-client
Does this method work?
Because of the way URL path matching is done in the library using Regular Expressions and MatchString against the HTTP request, the overall path matching in this library is defective.
For example, if I register a handler with the prefix "echo", all of below match and work even though only the first one should be working. As long at the last part of the URL ends with the prefix string, the library will allow that URL to be handled.
http://server:port/echo/
http://server:port/test/echo/
http://server:port/echo/test/test/echo/
PR #73 broke the ability to connect to a sockjs server based on this library from a html page launched as a local resource (file://) without use of web server.
This looks to be due to the addition of the check for incoming origin header "null" and then changing it to *. Earlier, when origin from client side is null, Access-Control-Allow-Origin was also set to "null" . But since its changed to *, Chrome does not allow it as a match for Origin "null"
I understand that sending "null" as allowed origin is not a good idea, but is there an alternative way get sockjs working form file:// resources. There are situations where we launch a html directly without web server.
Please release following commit as soon as possible.
90eb0f5#diff-59e9f77255aa4b5204cb9e9fcf9aa058
If I have something like the following:
http.Handle("/channel/", sockjs.NewHandler("/channel", sockjs.DefaultOptions, ChannelHandler))
http.Handle("/room/", sockjs.NewHandler("/room", sockjs.DefaultOptions, RoomHandler))
http.Handle("/all/", sockjs.NewHandler("/all", sockjs.DefaultOptions, AllHandler))
and I have some state which each route can read and write to, is a race condition applied? When I run go build -race it doesn't goive race errors, but I am not completely certain of this. Are the routes for example combined to that it is always run in one goroutine? So are these prefixes always in sync.
handler.go
has a global map for compiled regexes.
Can we change it to compile regex upfront and attach it to the handler itself?
There are several benefits:
I am happy to take this one if there are not some reasons that would blow my story away.
Is there any way the http.Request
used to establish a SockJS connection could be exposed? I don't even need the full Request
actually. All I really want is to be able to use path variables from the SockJS uri. For example, if my SockJS handler is wired up like:
func main() {
sockjsHandler := sockjs.NewHandler("/echo/{foo}", sockjs.DefaultOptions, echoHandler)
http.ListenAndServe("8081", sockjsHandler)
}
func echoHandler(session sockjs.Session) {
// I'd like to access the value of "foo" here.
}
Basically like how mux.Vars works.
Thoughts?
In order to read a message and convert it to JSON, I need to strip the surrounding quotes and remove \ escaping on the internal quotes.
msg, _ := conn.ReadMessage()
// Message is in the format: `"{\"fruit\":\"apple\"}"`.
// To convert to JSON it must be in the format `{"fruit":"apple"}`.
msg = []byte(strings.Replace(string(msg[1:len(msg)-1]), `\"`, `"`, -1))
I'm just sending a normal JSON request from a sockjs web client to the server. Is there something I'm doing incorrectly? This seems like a hack.
According to sockjs-client, XDR transports are used for IE8-9. Since the site I'm using this library for needs to be able to support these browsers and I need to maintain compatibility with sockjs-node, will they still be able to connect using XHR, considering XDR is just a modified version of it? If they can't, would it be possible to add support for it? I could give a hand if you wish.
I'm not sure if DecodeFrames in WriteMessage this makes sense. You can't write a JSON message to clients now or else it will double-encode it. Plus the config setting is called decode, not encode. WriteMessage encoding should probably be left to the user.
Thank you for nice lib.
I have implemented authentication wrapper that requires user to authenticate before he can do anything. I want to close a session that has not been authenticated for 5 seconds, so i added timeout to session handler. What i'm trying to understand is what happens when calling session Close while Recv blocked. Or if there is some better way to interrupt Recv?
when i use websocket not sockjs, i'm confused,
2020/11/18 19:16:14.854 [D] [asm_amd64.s:1373] A new sockjs session established ...
2020/11/18 19:16:14.854 [D] [asm_amd64.s:1373] l1kg4bq2
2020/11/18 19:16:14.854 [D] [asm_amd64.s:1373] &{{0 0} l1kg4bq2 1 0xc000020190 [] 0xc000010018 0xc000010020 0xc0000761e0 0xc00040a180 5000000000 25000000000 0xc0004180f0 0xc0000741e0}
&{{0 0} l1kg4bq2 1 0xc000020190 [] 0xc000010018 0xc000010020 0xc0000761e0 0xc00040a180 5000000000 25000000000 0xc0004180f0 0xc0000741e0}
****************
****************
2020/11/18 19:16:14.866 [E] [handler.go:31] sockjs: session not in open state
2020-11-18T19:16:14.866+0800 ERROR logger/zap.go:31 [session error]
git.sxjicheng.com/jicheng/trend_wsim/logger.Error
/Users/lcp0578/go/src/git.sxjicheng.com/jicheng/trend_wsim/logger/zap.go:31
github.com/lcp0578/trend_wsim/websocket.initConnection
/Users/lcp0578/go/src/git.sxjicheng.com/jicheng/trend_wsim/websocket/handler.go:258
github.com/lcp0578/trend_wsim/websocket.Handler
/Users/lcp0578/go/src/git.sxjicheng.com/jicheng/trend_wsim/websocket/handler.go:31
2020/11/18 19:16:14.866 [D] [asm_amd64.s:1373] init
go
handler := sockjs.NewHandler("/ws", sockjs.DefaultOptions, websocket.Handler)
func Handler(session sockjs.Session) {
logs.Debug("A new sockjs session established ...")
logs.Debug(session.ID())
logs.Debug(session)
sessionId := session.ID()
if sessionId == "" {
logs.Error("session id error")
return
}
//inline
key, err := initConnection(session)
logs.Debug("init")
logs.Debug(key)
logs.Debug(err)
if err != nil {
logs.Error(err)
return
}
//offline
defer closeConnect(key, sessionId)
// sub message
go subMessage(session, sessionId, key)
for {
if msg, err := session.Recv(); err == nil {
messageCenter(msg, session)
continue
}
break
}
}
func initConnection(session sockjs.Session) (key string, err error) {
msg, err := session.Recv()
if err != nil {
logs.Error(err)
logger.Error("session error")
return
}
logger.Debug("init msg")
logger.Debug(msg)
recvMsg, err := decodeMsg(msg)
logger.Debug(recvMsg)
if err != nil {
return
}
if recvMsg.Type != "connection" && recvMsg.Type != "reconnection" {
return "", errors.New("init connection type error")
}
key = strconv.Itoa(recvMsg.FromType) + "_" + strconv.Itoa(recvMsg.Data.Mine.Id) + "_" + strconv.Itoa(recvMsg.ToType) + "_" + strconv.Itoa(recvMsg.Data.To.Id)
logger.Debug(key)
chat.SetUserMap(key, session.ID())
logger.Debug("set user map")
return
}
uni-app:
uni.connectSocket({
url:"ws://127.0.0.1:8088/ws/92/l1kg4bq2/websocket",
header:{
"Connection": "Upgrade",
//"Sec-WebSocket-Accept": "J04QneyO/SnbBD1HyZJePaOLoFA=",
"Upgrade": "websocket"
},
complete: function(res) {
console.log(res);
},
success:function(res){
console.log(res);
console.log('WebSocket success!');
}
});
Running against latest master and on go version go1.9.2 linux/amd64
, I get the following panic. I don't know what causes it yet, perhaps xhr? Any help would be really appreciated as it only seems to show up in production :(
panic: net/http: CloseNotify called after ServeHTTP finished
goroutine 4804667 [running]:
net/http.(*response).CloseNotify(0xc42361a000, 0x9b4240)
#011/usr/lib/go-1.8/src/net/http/server.go:1902 +0xa3
/vendor/github.com/urfave/negroni.(*responseWriterCloseNotifer).CloseNotify(0xc42420a008, 0xc422d182a0)
#011/vendor/github.com/urfave/negroni/response_writer.go:112 +0x5a
/vendor/github.com/igm/sockjs-go/sockjs.newHTTPReceiver.func1(0x7ffb3d6dc530, 0xc42420a008, 0xc420ad85f0)
#011/vendor/github.com/igm/sockjs-go/sockjs/httpreceiver.go:46 +0x49
created by /vendor/github.com/igm/sockjs-go/sockjs.newHTTPReceiver
#011/vendor/github.com/igm/sockjs-go/sockjs/httpreceiver.go:57 +0x172
In the web socket mode, the send function hangs up at different times. In the library code comes to
w.conn.WriteMessage (websocket.TextMessage, [] byte (frame));
helps deadline w.conn.SetWriteDeadline(time.Now().Add(time.Second))
Why can this happen it? Add deadline please as soon as possible.
ATM, there is no way of restricting Methods for socksJS (e.g. If I don't want my application to use XHR)
My idea is to add a bitmask (using the ReceiverType)on the NewHandler function, the default would be all, if not default, then it would be only the types within the bitmask
sockjs-go works fine.
but I think it needs a method to get session id.
cause some apps need to make channel to group connections.
in that case, that method required to group and share with other programs (like web) as a key for each connections.
As vgo became accepted proposal let's add go.mod. Especially for v2, so that import path can be in the form vgo understands, ie;
import "github.com/igm/sockjs-go/v2/sockjs"
I have an existing Go application which uses gorilla/websocket
. I also have a test suite written in Go which uses gorilla/websocket
's websocket.Dialer
to connect to an instance of the application and exercise a bunch of functionality. This all works.
I replaced gorilla/websocket
with sockjs
in the application. Now my test client, using gorilla/websocket
can no longer connect to it. I always get bad handshake
. My actual handler never gets called -- I have a log statement as the first line, and it never logs anything.
I've tried these variations:
socketJSHandler := sockjs.NewHandler("/chat", sockjs.DefaultOptions, socketHandler)
socketJSHandler := sockjs.NewHandler("/", sockjs.DefaultOptions, socketHandler)
socketJSHandler := sockjs.NewHandler("/chat/", sockjs.DefaultOptions, socketHandler)
I've also tried using custom options instead of the defaults.
On the client side, I've tried to connect to:
ws://ip_address:9999/
ws://ip_address:9999/chat
ws://ip_address:9999/chat/
ws://ip_address:9999/chat/websocket
ws://ip_address:9999/chat/websocket/
http://ip_address:9999/
http://ip_address:9999/chat
http://ip_address:9999/chat/
http://ip_address:9999/chat/websocket
http://ip_address:9999/chat/websocket/
So, my socketHandler
function never gets called, and gorilla/websockets
gets a "bad handshake" message every time I connect to sockjs
. I'm using the same version of gorilla/websockets
in all places.
Am I doing something wrong, or is this possibly a bug?
I'm using v2 and looked around the source but I can't seem to find anything to grab the request or clients IP from a sockjs.Session instance?
I'm referring to a server implementation to work with this client lib: https://github.com/sockjs/websocket-multiplex.
Has anyone tried to implement TLS/SSL for this project? I found this stack overflow question for nodejs: http://stackoverflow.com/questions/10523947/setting-up-ssl-with-sockjs
It also seems to improve connectivity, especially on iOS (where iOS 9 is now forcing default connections to be HTTPS): https://gist.github.com/konklone/4247942
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.