2016-03-05 02:08:16 +03:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/tidwall/tile38/client"
|
2016-03-06 17:55:00 +03:00
|
|
|
"github.com/tidwall/tile38/controller/log"
|
|
|
|
"github.com/tidwall/tile38/core"
|
2016-03-05 02:08:16 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var errCloseHTTP = errors.New("close http")
|
|
|
|
|
|
|
|
// ListenAndServe starts a tile38 server at the specified address.
|
|
|
|
func ListenAndServe(
|
2016-03-05 23:39:36 +03:00
|
|
|
host string, port int,
|
2016-03-05 02:08:16 +03:00
|
|
|
handler func(command []byte, conn net.Conn, rd *bufio.Reader, w io.Writer, websocket bool) error,
|
|
|
|
) error {
|
2016-03-05 23:39:36 +03:00
|
|
|
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
|
2016-03-05 02:08:16 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Infof("The server is now ready to accept connections on port %d", port)
|
|
|
|
for {
|
|
|
|
conn, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
go handleConn(conn, handler)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleConn(
|
|
|
|
conn net.Conn,
|
|
|
|
handler func(command []byte, conn net.Conn, rd *bufio.Reader, w io.Writer, websocket bool) error,
|
|
|
|
) {
|
2016-03-06 17:55:00 +03:00
|
|
|
if core.ShowDebugMessages {
|
2016-03-05 02:08:16 +03:00
|
|
|
addr := conn.RemoteAddr().String()
|
|
|
|
log.Debugf("opened connection: %s", addr)
|
|
|
|
defer func() {
|
|
|
|
log.Debugf("closed connection: %s", addr)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
rd := bufio.NewReader(conn)
|
|
|
|
for i := 0; ; i++ {
|
|
|
|
err := func() error {
|
|
|
|
command, proto, err := client.ReadMessage(rd, conn)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(command) > 0 && (command[0] == 'Q' || command[0] == 'q') && strings.ToLower(string(command)) == "quit" {
|
|
|
|
return io.EOF
|
|
|
|
}
|
|
|
|
var b bytes.Buffer
|
|
|
|
if err := handler(command, conn, rd, &b, proto == client.WebSocket); err != nil {
|
|
|
|
if proto == client.HTTP {
|
|
|
|
conn.Write([]byte(`HTTP/1.1 500 ` + err.Error() + "\r\nConnection: close\r\n\r\n"))
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
switch proto {
|
|
|
|
case client.Native:
|
|
|
|
if err := client.WriteMessage(conn, b.Bytes()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case client.HTTP:
|
|
|
|
if err := client.WriteHTTP(conn, b.Bytes()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return errCloseHTTP
|
|
|
|
case client.WebSocket:
|
|
|
|
if err := client.WriteWebSocket(conn, b.Bytes()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := conn.Write([]byte{137, 0}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return errCloseHTTP
|
|
|
|
default:
|
|
|
|
b.WriteString("\r\n")
|
|
|
|
if _, err := conn.Write(b.Bytes()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}()
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err == errCloseHTTP ||
|
|
|
|
strings.Contains(err.Error(), "use of closed network connection") {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|