Merge pull request #14 from edmund-huber/websocket

add support for WebSocket connections
This commit is contained in:
Edmund Huber 2015-06-09 10:30:04 -07:00
commit 67a6247377
4 changed files with 89 additions and 0 deletions

View File

@ -3,6 +3,7 @@ name = "irc.example.com" ; required, usually a hostname
database = "ergonomadic.db" ; path relative to this file database = "ergonomadic.db" ; path relative to this file
listen = "localhost:6667" ; see `net.Listen` for examples listen = "localhost:6667" ; see `net.Listen` for examples
listen = "[::1]:6667" ; multiple `listen`s are allowed. listen = "[::1]:6667" ; multiple `listen`s are allowed.
wslisten = ":8080" ; websocket listen
log = "debug" ; error, warn, info, debug log = "debug" ; error, warn, info, debug
motd = "motd.txt" ; path relative to this file motd = "motd.txt" ; path relative to this file
password = "JDJhJDA0JHJzVFFlNXdOUXNhLmtkSGRUQVVEVHVYWXRKUmdNQ3FKVTRrczRSMTlSWGRPZHRSMVRzQmtt" ; 'test' password = "JDJhJDA0JHJzVFFlNXdOUXNhLmtkSGRUQVVEVHVYWXRKUmdNQ3FKVTRrczRSMTlSWGRPZHRSMVRzQmtt" ; 'test'

View File

@ -23,6 +23,7 @@ type Config struct {
PassConfig PassConfig
Database string Database string
Listen []string Listen []string
Wslisten string
Log string Log string
MOTD string MOTD string
Name string Name string

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"net/http"
"os" "os"
"os/signal" "os/signal"
"strings" "strings"
@ -72,6 +73,10 @@ func NewServer(config *Config) *Server {
server.listen(addr) server.listen(addr)
} }
if config.Server.Wslisten != "" {
server.wslisten(config.Server.Wslisten)
}
signal.Notify(server.signals, SERVER_SIGNALS...) signal.Notify(server.signals, SERVER_SIGNALS...)
return server return server
@ -203,6 +208,41 @@ func (s *Server) listen(addr string) {
}() }()
} }
//
// websocket listen goroutine
//
func (s *Server) wslisten(addr string) {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
Log.error.Printf("%s method not allowed", s)
return
}
// We don't have any subprotocols, so if someone attempts to `new
// WebSocket(server, "subprotocol")` they'll break here, instead of
// getting the default, ambiguous, response from gorilla.
if v, ok := r.Header["Sec-Websocket-Protocol"]; ok {
http.Error(w, fmt.Sprintf("WebSocket subprocotols (e.g. %s) not supported", v), 400)
}
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
Log.error.Printf("%s websocket upgrade error: %s", s, err)
return
}
s.newConns <- WSContainer{ws}
})
go func() {
Log.info.Printf("%s listening on %s", s, addr)
err := http.ListenAndServe(addr, nil)
if err != nil {
Log.error.Printf("%s listenAndServe error: %s", s, err)
}
}()
}
// //
// server functionality // server functionality
// //

47
irc/websocket.go Normal file
View File

@ -0,0 +1,47 @@
package irc
import (
"github.com/gorilla/websocket"
"net/http"
"time"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// If a WS session contains sensitive information, and you choose to use
// cookies for authentication (during the HTTP(S) upgrade request), then
// you should check that Origin is a domain under your control. If it
// isn't, then it is possible for users of your site, visiting a naughty
// Origin, to have a WS opened using their credentials. See
// http://www.christian-schneider.net/CrossSiteWebSocketHijacking.html#main.
// We don't care about Origin because the (IRC) authentication is contained
// in the WS stream -- the WS session is not privileged when it is opened.
CheckOrigin: func(r *http.Request) bool { return true },
}
type WSContainer struct {
*websocket.Conn
}
func (this WSContainer) Read(msg []byte) (int, error) {
ty, bytes, err := this.ReadMessage()
if ty == websocket.TextMessage {
n := copy(msg, []byte(string(bytes)+CRLF+CRLF))
return n, err
}
// Binary, and other kinds of messages, are thrown away.
return 0, nil
}
func (this WSContainer) Write(msg []byte) (int, error) {
err := this.WriteMessage(websocket.TextMessage, msg)
return len(msg), err
}
func (this WSContainer) SetDeadline(t time.Time) error {
if err := this.SetWriteDeadline(t); err != nil {
return err
}
return this.SetReadDeadline(t)
}