diff --git a/chatserver.go b/chatserver.go index 121a739..1b118ea 100644 --- a/chatserver.go +++ b/chatserver.go @@ -1,23 +1,56 @@ package main +// Lot's of learning right out of the gate: +// https://stackoverflow.com/questions/51472020/how-to-get-the-size-of-available-tcp-data + import ( + "bufio" "flag" "fmt" "io" "net" "os" "strconv" + "sync" "time" ) +type bufferedConn struct { + r *bufio.Reader + rout io.Reader + net.Conn +} + +func newBufferedConn(c net.Conn) bufferedConn { + return bufferedConn{bufio.NewReader(c), nil, c} +} + +func (b bufferedConn) Peek(n int) ([]byte, error) { + return b.r.Peek(n) +} + +func (b bufferedConn) Buffered() (int) { + return b.r.Buffered() +} + +func (b bufferedConn) Read(p []byte) (int, error) { + if b.rout != nil { + return b.rout.Read(p) + } + return b.r.Read(p) +} + type myMsg struct { sender net.Conn bytes []byte receivedAt time.Time } +var firstMsgs chan myMsg var myMsgs chan myMsg -var myConns map[net.Conn]bool +var myUnsortedConns map[net.Conn]bool +var myRawConns map[net.Conn]bool +var newConns chan net.Conn func usage() { fmt.Fprintf(os.Stderr, "\nusage: go run chatserver.go\n") @@ -27,14 +60,11 @@ func usage() { os.Exit(1) } -func handleConnection(conn net.Conn) { - // Why don't these work? - //buf := make([]byte, 0, 1024) - //buf := []byte{} - // But this does - fmt.Fprintf(conn, "Welcome! This is an open relay chat server. There is no security yet.\n") +func handleRaw(conn bufferedConn) { + // Handle all subsequent packets buf := make([]byte, 1024) for { + fmt.Fprintf(os.Stdout, "[raw] Waiting for message...\n"); count, err := conn.Read(buf) if nil != err { if io.EOF != err { @@ -43,8 +73,54 @@ func handleConnection(conn net.Conn) { fmt.Fprintf(os.Stdout, "Ending socket\n") break } - // not so sure about why this case exists - // we'll just ignore it for now... + // Fun fact: if the buffer's current length (not capacity) is 0 + // then the Read returns 0 without error + if 0 == count { + fmt.Fprintf(os.Stdout, "Weird") + continue + } + fmt.Fprintf(os.Stdout, "Queing message...\n"); + myMsgs <- myMsg{ + receivedAt: time.Now(), + sender: conn, + bytes: buf[0:count], + } + } +} + +func handleSorted(conn bufferedConn) { + // at this piont we've already at least one byte via Peek() + // so the first packet is available in the buffer + n := conn.Buffered() + firstMsg, err := conn.Peek(n) + if nil != err { + panic(err) + } + firstMsgs <- myMsg{ + receivedAt: time.Now(), + sender: conn, + bytes: firstMsg, + } + + // TODO + // * TCP-CHAT + // * HTTP + // * TLS + + // Handle all subsequent packets + buf := make([]byte, 1024) + for { + fmt.Fprintf(os.Stdout, "[sortable] Waiting for message...\n"); + count, err := conn.Read(buf) + if nil != err { + if io.EOF != err { + fmt.Fprintf(os.Stderr, "Non-EOF socket error: %s\n", err) + } + fmt.Fprintf(os.Stdout, "Ending socket\n") + break + } + // Fun fact: if the buffer's current length (not capacity) is 0 + // then the Read returns 0 without error if 0 == count { // fmt.Fprintf(os.Stdout, "Weird") continue @@ -57,9 +133,57 @@ func handleConnection(conn net.Conn) { } } +// TODO https://github.com/polvi/sni +func handleConnection(conn net.Conn) { + fmt.Fprintf(os.Stdout, "Accepting socket\n") + + m := sync.Mutex{} + virgin := true + myUnsortedConns[conn] = true + + // Why don't these work? + //buf := make([]byte, 0, 1024) + //buf := []byte{} + // But this does + + bufConn := newBufferedConn(conn) + go func() { + // Handle First Packet + fmsg, err := bufConn.Peek(1) + if nil != err { + panic(err) + } + fmt.Fprintf(os.Stdout, "[First Byte] %s\n", fmsg) + + m.Lock(); + if virgin { + virgin = false + go handleSorted(bufConn) + } else { + go handleRaw(bufConn) + } + m.Unlock(); + }() + + time.Sleep(250 * 1000000) + // If we still haven't received data from the client + // assume that the client must be expecting a welcome from us + m.Lock() + if virgin { + virgin = false + // don't block for this + // let it be handled after the unlock + defer fmt.Fprintf(conn, "Welcome! This is an open relay chat server. There is no security yet.\n") + } + m.Unlock() +} + func main() { + firstMsgs = make(chan myMsg, 128) myMsgs = make(chan myMsg, 128) - myConns = make(map[net.Conn]bool) + newConns = make(chan net.Conn, 128) + myRawConns = make(map[net.Conn]bool) + myUnsortedConns = make(map[net.Conn]bool) flag.Usage = usage port:= flag.Uint("telnet-port", 4080, "tcp telnet chat port") flag.Parse() @@ -76,8 +200,21 @@ func main() { go func() { for { - fmt.Fprintf(os.Stdout, "Waiting for message...\n"); - msg := <- myMsgs + conn, err := sock.Accept() + if err != nil { + // Not sure what kind of error this could be or how it could happen. + // Could a connection abort or end before it's handled? + fmt.Fprintf(os.Stderr, "Error accepting connection:\n%s\n", err) + } + newConns <- conn + } + }() + + for { + select { + case conn := <- newConns: + go handleConnection(conn) + case msg := <- myMsgs: ts, err := msg.receivedAt.MarshalJSON() if nil != err { fmt.Fprintf(os.Stderr, "[Error] %s\n", err) @@ -85,7 +222,7 @@ func main() { fmt.Fprintf(os.Stdout, "[Timestamp] %s\n", ts) fmt.Fprintf(os.Stdout, "[Remote] %s\n", msg.sender.RemoteAddr().String()) fmt.Fprintf(os.Stdout, "[Message] %s\n", msg.bytes); - for conn, _ := range myConns { + for conn, _ := range myRawConns { if msg.sender == conn { continue } @@ -95,18 +232,15 @@ func main() { // SetDeadTime and Disconnect them? conn.Write(msg.bytes) } + case msg := <- firstMsgs: + fmt.Fprintf(os.Stdout, "f [First Message]\n") + ts, err := msg.receivedAt.MarshalJSON() + if nil != err { + fmt.Fprintf(os.Stderr, "f [Error] %s\n", err) + } + fmt.Fprintf(os.Stdout, "f [Timestamp] %s\n", ts) + fmt.Fprintf(os.Stdout, "f [Remote] %s\n", msg.sender.RemoteAddr().String()) + fmt.Fprintf(os.Stdout, "f [Message] %s\n", msg.bytes); } - }() - - for { - conn, err := sock.Accept() - if err != nil { - // Not sure what kind of error this could be or how it could happen. - // Could a connection abort or end before it's handled? - fmt.Fprintf(os.Stderr, "Error accepting connection:\n%s\n", err) - } - fmt.Fprintf(os.Stdout, "Accepting socket\n") - myConns[conn] = true - go handleConnection(conn) } }