-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathserver.go
112 lines (104 loc) · 2.72 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package main
// https://coderwall.com/p/wohavg/creating-a-simple-tcp-server-in-go
import (
"bufio"
"fmt"
"golang-redis-mock/commands"
"golang-redis-mock/resp"
"net"
"os"
"strings"
)
// Redis server constants
const (
RedisHost = "localhost"
RedisPort = "6382"
connType = "tcp"
)
// RunClient runs a session that takes user input and makes socket connection
// to server
func runClient() {
// connect to this socket
conn, _ := net.Dial("tcp", fmt.Sprintf("%s:%s", RedisHost, RedisPort))
for {
// read in input from stdin
reader := bufio.NewReader(os.Stdin)
fmt.Print("redis-cli> ")
text, _ := reader.ReadString('\n')
text = strings.Trim(text, "\n")
// Redis server accepts RESPArray(RESPBulkString)
parts := strings.Split(text, " ")
commandArray := make([]string, len(parts))
for i := 0; i < len(parts); i++ {
part := parts[i]
// Get length of part
commandArray[i] = fmt.Sprintf("$%d\r\n%s\r\n", len(part), part)
}
cmd := fmt.Sprintf("*%d\r\n", len(commandArray)) + strings.Join(commandArray, "")
// send to socket
fmt.Fprintf(conn, cmd)
// listen for reply
message, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Print(message)
}
}
func main() {
// Listen for incoming connections.
l, err := net.Listen(connType, RedisHost+":"+RedisPort)
if err != nil {
fmt.Println("Error listening:", err.Error())
os.Exit(1)
}
// Close the listener when the application closes.
defer l.Close()
fmt.Println("Listening on " + RedisHost + ":" + RedisPort)
// Run client
go runClient()
for {
// Listen for an incoming connection.
conn, err := l.Accept()
if err != nil {
fmt.Println("Error accepting: ", err.Error())
os.Exit(1)
}
// Handle connections in a new goroutine.
go handleRequest(conn)
}
}
// takeFullInput is a custom splitfunc that takes in the full CRLF feed for processing.
func takeFullInput(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if atEOF == true {
return 0, []byte{}, nil
}
return len(data), data, nil
}
// Handles incoming requests.
func handleRequest(conn net.Conn) {
defer conn.Close()
// Create a new reader
scanner := bufio.NewScanner(conn)
scanner.Split(takeFullInput)
for scanner.Scan() {
bytes := scanner.Bytes()
ras, _, f := resp.ParseRedisClientRequest(bytes)
if f == resp.EmptyRedisError {
for _, ra := range ras {
dataType, err := commands.ExecuteStringCommand(ra)
if err != resp.EmptyRedisError {
conn.Write([]byte(err.ToString() + "\n"))
} else {
if dataType == nil {
conn.Write([]byte("(nil)" + "\n"))
} else {
conn.Write([]byte(dataType.ToString() + "\n"))
}
}
}
} else {
conn.Write([]byte(f.ToString() + "\n"))
}
}
}