-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Closed
Closed
Copy link
Labels
FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone
Description
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (go version
)?
go version go1.8.3 windows/amd64
What operating system and processor architecture are you using (go env
)?
set GOARCH=amd64
set GOBIN=
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows (win7)
set GOOS=windows
What did you do?
i use gorilla/websocket, and when Upgrade, it will be blocked in Hijack. the sample code of echo in gorilla/websocket will blocked sometimes on windows, and if i add a line time.Sleep(10 * time.Millisecond)
before Upgrade, then it will almost 100% reproduce.
the blocked goroutine stack just like this:
goroutine 36 [semacquire]:
sync.runtime_notifyListWait(0xc04214a190, 0x0)
D:/Go/src/runtime/sema.go:298 +0x119
sync.(*Cond).Wait(0xc04214a180)
D:/Go/src/sync/cond.go:57 +0x90
net/http.(*connReader).abortPendingRead(0xc04214a080)
D:/Go/src/net/http/server.go:686 +0xc6
net/http.(*conn).hijackLocked(0xc04213c000, 0x7f0f68, 0xc04213c090, 0x338270, 0xc04215f9b0, 0x40de64)
D:/Go/src/net/http/server.go:292 +0x5a
net/http.(*response).Hijack(0xc04208e0e0, 0x0, 0x0, 0x0, 0x0, 0x0)
D:/Go/src/net/http/server.go:1892 +0x103
github.com/gorilla/websocket.(*Upgrader).Upgrade(0x98d9c0, 0x9609c0, 0xc04208e0e0, 0xc042142100, 0x0, 0x0, 0x1, 0x1)
e:/gopath/src/github.com/gorilla/websocket/server.go:164 +0x3fe
nd/sparklet/net3.StartWebSocketServer.func1(0x9609c0, 0xc04208e0e0, 0xc042142100)
E:/work/goweb/src/nd/sparklet/net3/api.go:63 +0x10f
net/http.HandlerFunc.ServeHTTP(0xc042043b70, 0x9609c0, 0xc04208e0e0, 0xc042142100)
D:/Go/src/net/http/server.go:1942 +0x4b
net/http.(*ServeMux).ServeHTTP(0xc0420717d0, 0x9609c0, 0xc04208e0e0, 0xc042142100)
D:/Go/src/net/http/server.go:2238 +0x137
net/http.serverHandler.ServeHTTP(0xc042118000, 0x9609c0, 0xc04208e0e0, 0xc042142100)
D:/Go/src/net/http/server.go:2568 +0x99
net/http.(*conn).serve(0xc04213c000, 0x960fc0, 0xc04214a040)
D:/Go/src/net/http/server.go:1825 +0x619
created by net/http.(*Server).Serve
D:/Go/src/net/http/server.go:2668 +0x2d5
What did you expect to see?
Upgrade will return soon
What did you see instead?
Upgrade blocked forever
similar discussion in gorilla/websocket, please see this issue
the sample whole code is below:
it seems will only blocked on windows, it runs fine on linux.
server.go
package main
import (
"flag"
"html/template"
"log"
"net/http"
"github.com/gorilla/websocket"
"time"
)
var addr = flag.String("addr", "localhost:8080", "http service address")
var upgrader = websocket.Upgrader{} // use default options
func echo(w http.ResponseWriter, r *http.Request) {
time.Sleep(10 * time.Millisecond)
c, err := upgrader.Upgrade(w, r, nil) //this is will be blocked in Hijack!!!!!!
if err != nil {
log.Print("upgrade:", err)
return
}
defer c.Close()
for {
mt, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)
err = c.WriteMessage(mt, message)
if err != nil {
log.Println("write:", err)
break
}
}
}
func home(w http.ResponseWriter, r *http.Request) {
homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}
func main() {
flag.Parse()
log.SetFlags(0)
http.HandleFunc("/echo", echo)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe(*addr, nil))
}
var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
var output = document.getElementById("output");
var input = document.getElementById("input");
var ws;
var print = function(message) {
var d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("{{.}}");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>
`))
client.go
package main
import (
"flag"
"log"
"net/url"
"os"
"os/signal"
"time"
"github.com/gorilla/websocket"
)
var addr = flag.String("addr", "localhost:8080", "http service address")
func main() {
flag.Parse()
log.SetFlags(0)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
done := make(chan struct{})
go func() {
defer c.Close()
defer close(done)
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
return
}
log.Printf("recv: %s", message)
}
}()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case t := <-ticker.C:
err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
if err != nil {
log.Println("write:", err)
return
}
case <-interrupt:
log.Println("interrupt")
// To cleanly close a connection, a client should send a close
// frame and wait for the server to close the connection.
err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("write close:", err)
return
}
select {
case <-done:
case <-time.After(time.Second):
}
c.Close()
return
}
}
}
Metadata
Metadata
Assignees
Labels
FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.