gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
https://gin-gonic.com/
MIT License
77.96k stars 7.97k forks source link

response.WriteHeader on hijacked connection #189

Closed gplume closed 9 years ago

gplume commented 9 years ago

Hi there, I have a new bug related to Gin using websockets with either the gorillatoolkit or sockjs (ws emulation). If the user reload a page or if I use the socket system on more than one page (obviously not on an SPA site) I got this error :

http: response.WriteHeader on hijacked connection

...every time I hit reload. If I do that many times then the socket messages begins to lag, sometimes more than 1 minute !

I'm sure it's somewhat gin related because I've tried the same setup with httprouter only and it never show the same error message.

Perhaps just for this web socket route I should bypass the Gin Context but is that possible ? (I mean using an handler function with: _(w http.ResponseWriter, r *http.Request, _ httprouter.Params)) ?

javierprovecho commented 9 years ago

Hello, thank you for reporting this. Can you post your example and use case source code? I'll take a look into it.

As of bypass the Gin Context, you can do it if you modify the engine variable from unexported to exported in Gin source code.

— Enviado desde Mailbox

On Fri, Jan 2, 2015 at 10:34 PM, gplume notifications@github.com wrote:

Hi there, I have a new bug related to Gin using websockets with either the gorillatoolkit or sockjs (ws emulation). If the user reload a page or if I use the socket system on more than one page (obviously not on an SPA site) I got this error : http: response.WriteHeader on hijacked connection ...every time I hit reload. If I do that many times then the socket messages begins to lag, sometimes more than 1 minute ! I'm sure it's somewhat gin related because I've tried the same setup with httprouter only and it never show the same error message.

Perhaps just for this web socket route I should bypass the Gin Context but is that possible ? (I mean using an handler function with: _(w http.ResponseWriter, r *http.Request, _ httprouter.Params)) ?

Reply to this email directly or view it on GitHub: https://github.com/gin-gonic/gin/issues/189

gplume commented 9 years ago

Although I've tried to reduce both examples (httprouter vs. Gin) at their minimal form it's still too big to post it here as I copy the gorilla long chat example into it so there's no issues with this part of code, how can I send you the files ?

javierprovecho commented 9 years ago

Submit a GitHub Gist and post the link here. Better than sending emails. ;)

gplume commented 9 years ago

I've submitted two Gist and then: One of our mostly harmless robots seems to think you are not a human. ... so my gist are not available till I recover a full account I guess...

gplume commented 9 years ago

So I'm Human again :-) Here’s the gist with minimal examples that does nothing more than connecting through web socket. Reload the page on the browser and watch the terminal console. Perhaps the Httprouter is less verbose and still see the warning but stay shut ?

Gin based : https://gist.github.com/gplume/a52e1f0d282920769dec

Httprouter: https://gist.github.com/gplume/052d86b1e5176c89dc85

Thanks for your time!

javierprovecho commented 9 years ago

@gplume thank you, I'm already reviewing it. About the warning, httprouter by default does not output anything, but the warning is still there.

gplume commented 9 years ago

@javierprovecho thank you for reviewing so fast (and also for the last week on gin). How do you make httprouter talk ?

javierprovecho commented 9 years ago

@gplume there is no way rather than using middleware for making httprouter talk, because it uses func ListenAndServe(addr string, handler Handler) error

I found why that warning is printed. It is all about how Gorilla's WebSocket is implemented:

Once all the headers are good, it hijacks the http connection. Hijacking means taking over the underlying TCP connection and starting using as another protocol, i.e. websocket connection.

Full Article

However this leaves us with the question about performance and lagging, I'll keep investigating.

gplume commented 9 years ago

So the sockJS use the same Hijacking method because that's what made me found this warning. Would you like a SockJS Gist too ? (client & server, integrated with a pubSub Go package too) If I understand well it's just a warning and I must say that the long lagging issue was just with the sockjs implementation, not the gorilla web socket, although my test with that last one was much less extensive.

What is curious is that with both method Gin only whines on page reload, the first module load is okay and the warning never appears on a single page Application, for example.

@javierprovecho Thanks for investigation though !

javierprovecho commented 9 years ago

@gplume some debugging I made using netstat:

Here this is executed just after first request.

javprov@computer:~/Documentos/go/ginsocket$ netstat -ant | grep :8000
tcp        0      0 127.0.0.1:57924         127.0.0.1:8000          ESTABLECIDO
tcp        0      0 127.0.0.1:57922         127.0.0.1:8000          ESTABLECIDO
tcp6       0      0 :::8000                 :::*                    ESCUCHAR   
tcp6       0      0 127.0.0.1:8000          127.0.0.1:57922         ESTABLECIDO
tcp6       0      0 127.0.0.1:8000          127.0.0.1:57924         ESTABLECIDO

This other is after second request.

javprov@computer:~/Documentos/go/ginsocket$ netstat -ant | grep :8000
tcp        0      0 127.0.0.1:57927         127.0.0.1:8000          ESTABLECIDO
tcp        0      0 127.0.0.1:57922         127.0.0.1:8000          ESTABLECIDO
tcp6       0      0 :::8000                 :::*                    ESCUCHAR   
tcp6       0      0 127.0.0.1:8000          127.0.0.1:57922         ESTABLECIDO
tcp6       0      0 127.0.0.1:8000          127.0.0.1:57927         ESTABLECIDO
tcp6       0      0 127.0.0.1:8000          127.0.0.1:57924         TIME_WAIT  

(ESTABLECIDO means established, and ESCUCHAR means listening)

And here is the log for Gin after above.

javprov@computer:~/Documentos/go/ginsocket$ ./ginsocket 
[GIN-debug] GET   /                         --> main.ServeChat (1 handlers)
[GIN-debug] GET   /api/messages             --> main.ServeWs (1 handlers)
[GIN-debug] Listening and serving HTTP on :80002015/01/04 00:52:32 http: response.WriteHeader on hijacked connection

In my opinion, it is a strange way of reusing context, not sure. Anyway I think it is the only way for this frameworks (Gorilla and so) to work.

phrozen commented 9 years ago

I ran into this problem today while checking the same code on Gorilla Toolkit Chat example. Although it works on Go's http server code. Is there a work around using Gin? Like I dunno duplicating the request and response from the context?

Edit: is fixed here 251b73fc70c4f75487ec827be7b0c3a3e877e11a I think it was because of the headers being written.