|
package main |
|
|
|
import ( |
|
"crypto/rand" |
|
"html/template" |
|
"log" |
|
"math/big" |
|
"net/http" |
|
"sync" |
|
|
|
"github.com/gorilla/websocket" |
|
"flag" |
|
) |
|
|
|
const addrDefault = "localhost:8080" |
|
const bitSize = 128 |
|
|
|
var addr = flag.String("addr", addrDefault, "http service address") |
|
|
|
func main() { |
|
flag.Parse() |
|
log.SetFlags(0) |
|
|
|
server := NewServer() |
|
http.HandleFunc("/echo", server.echo) |
|
http.HandleFunc("/", server.home) |
|
log.Fatal(http.ListenAndServe(*addr, nil)) |
|
} |
|
|
|
type Server struct { |
|
upgrader websocket.Upgrader |
|
usedNumbers map[string]*big.Int |
|
mu sync.Mutex |
|
} |
|
|
|
func NewServer() *Server { |
|
return &Server{ |
|
upgrader: websocket.Upgrader{ |
|
CheckOrigin: func(r *http.Request) bool { |
|
return true // Adjust according to your CORS policy |
|
}, |
|
}, |
|
usedNumbers: make(map[string]*big.Int), |
|
} |
|
} |
|
|
|
func (s *Server) echo(w http.ResponseWriter, r *http.Request) { |
|
c, err := s.upgrader.Upgrade(w, r, nil) |
|
if err != nil { |
|
log.Printf("upgrade error: %v", err) |
|
return |
|
} |
|
defer c.Close() |
|
|
|
for { |
|
mt, message, err := c.ReadMessage() |
|
if err != nil { |
|
log.Printf("read error: %v", err) |
|
break |
|
} |
|
|
|
log.Printf("recv: %s", message) |
|
|
|
response, err := s.generateUniqueRandomBigInt() |
|
if err != nil { |
|
log.Printf("unique bigint generation error: %v", err) |
|
break |
|
} |
|
|
|
err = c.WriteMessage(mt, []byte(response.String())) |
|
if err != nil { |
|
log.Printf("write error: %v", err) |
|
break |
|
} |
|
} |
|
} |
|
|
|
func (s *Server) generateUniqueRandomBigInt() (*big.Int, error) { |
|
s.mu.Lock() |
|
defer s.mu.Unlock() |
|
|
|
for { |
|
num, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), bitSize)) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if _, exists := s.usedNumbers[num.String()]; !exists { |
|
s.usedNumbers[num.String()] = num |
|
return num, nil |
|
} |
|
} |
|
} |
|
|
|
func (s *Server) home(w http.ResponseWriter, r *http.Request) { |
|
err := homeTemplate.Execute(w, "ws://"+r.Host+"/echo") |
|
if err != nil { |
|
log.Printf("template execution error: %v", err) |
|
} |
|
} |
|
|
|
var homeTemplate = template.Must(template.New("").Parse(` |
|
<!DOCTYPE html> |
|
<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.textContent = message; |
|
output.appendChild(d); |
|
output.scroll(0, output.scrollHeight); |
|
}; |
|
|
|
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" style="max-height: 70vh;overflow-y: scroll;"></div> |
|
</td></tr></table> |
|
</body> |
|
</html> |
|
`)) |