summaryrefslogtreecommitdiff
path: root/deprecated-webircgateway/pkg/webircgateway/transport_websocket.go
diff options
context:
space:
mode:
Diffstat (limited to 'deprecated-webircgateway/pkg/webircgateway/transport_websocket.go')
-rw-r--r--deprecated-webircgateway/pkg/webircgateway/transport_websocket.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/deprecated-webircgateway/pkg/webircgateway/transport_websocket.go b/deprecated-webircgateway/pkg/webircgateway/transport_websocket.go
new file mode 100644
index 0000000..960718b
--- /dev/null
+++ b/deprecated-webircgateway/pkg/webircgateway/transport_websocket.go
@@ -0,0 +1,126 @@
+package webircgateway
+
+import (
+ "fmt"
+ "net"
+ "net/http"
+ "strings"
+ "sync"
+
+ "golang.org/x/net/websocket"
+)
+
+type TransportWebsocket struct {
+ gateway *Gateway
+ wsServer *websocket.Server
+}
+
+func (t *TransportWebsocket) Init(g *Gateway) {
+ t.gateway = g
+ t.wsServer = &websocket.Server{Handler: t.websocketHandler, Handshake: t.checkOrigin}
+ t.gateway.HttpRouter.Handle("/webirc/websocket/", t.wsServer)
+}
+
+func (t *TransportWebsocket) checkOrigin(config *websocket.Config, req *http.Request) (err error) {
+ config.Origin, err = websocket.Origin(config, req)
+
+ var origin string
+ if config.Origin != nil {
+ origin = config.Origin.String()
+ } else {
+ origin = ""
+ }
+
+ if !t.gateway.IsClientOriginAllowed(origin) {
+ err = fmt.Errorf("Origin %#v not allowed", origin)
+ t.gateway.Log(2, "%s. Closing connection", err)
+ return err
+ }
+
+ return err
+}
+
+func (t *TransportWebsocket) websocketHandler(ws *websocket.Conn) {
+ client := t.gateway.NewClient()
+
+ client.RemoteAddr = t.gateway.GetRemoteAddressFromRequest(ws.Request()).String()
+
+ clientHostnames, err := net.LookupAddr(client.RemoteAddr)
+ if err != nil {
+ client.RemoteHostname = client.RemoteAddr
+ } else {
+ // FQDNs include a . at the end. Strip it out
+ potentialHostname := strings.Trim(clientHostnames[0], ".")
+
+ // Must check that the resolved hostname also resolves back to the users IP
+ addr, err := net.LookupIP(potentialHostname)
+ if err == nil && len(addr) == 1 && addr[0].String() == client.RemoteAddr {
+ client.RemoteHostname = potentialHostname
+ } else {
+ client.RemoteHostname = client.RemoteAddr
+ }
+ }
+
+ if t.gateway.isRequestSecure(ws.Request()) {
+ client.Tags["secure"] = ""
+ }
+
+ _, remoteAddrPort, _ := net.SplitHostPort(ws.Request().RemoteAddr)
+ client.Tags["remote-port"] = remoteAddrPort
+
+ client.Log(2, "New websocket client on %s from %s %s", ws.Request().Host, client.RemoteAddr, client.RemoteHostname)
+ client.Ready()
+
+ // We wait until the client send queue has been drained
+ var sendDrained sync.WaitGroup
+ sendDrained.Add(1)
+
+ // Read from websocket
+ go func() {
+ for {
+ r := make([]byte, 1024)
+ len, err := ws.Read(r)
+ if err == nil && len > 0 {
+ message := string(r[:len])
+ client.Log(1, "client->: %s", message)
+ select {
+ case client.Recv <- message:
+ default:
+ client.Log(3, "Recv queue full. Dropping data")
+ // TODO: Should this really just drop the data or close the connection?
+ }
+
+ } else if err != nil {
+ client.Log(1, "Websocket connection closed (%s)", err.Error())
+ break
+
+ } else if len == 0 {
+ client.Log(1, "Got 0 bytes from websocket")
+ }
+ }
+
+ close(client.Recv)
+ }()
+
+ // Process signals for the client
+ for {
+ signal, ok := <-client.Signals
+ if !ok {
+ sendDrained.Done()
+ break
+ }
+
+ if signal[0] == "data" {
+ line := strings.Trim(signal[1], "\r\n")
+ client.Log(1, "->ws: %s", line)
+ ws.Write([]byte(line))
+ }
+
+ if signal[0] == "state" && signal[1] == "closed" {
+ ws.Close()
+ }
+ }
+
+ sendDrained.Wait()
+ ws.Close()
+}