evio/examples/redis-server/main.go

210 lines
5.3 KiB
Go
Raw Normal View History

2017-11-02 18:08:18 +03:00
// Copyright 2017 Joshua J Baker. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
2017-07-04 06:39:18 +03:00
package main
import (
2017-11-02 23:29:13 +03:00
"flag"
"fmt"
2017-07-04 06:39:18 +03:00
"log"
2017-11-02 03:36:35 +03:00
"net"
2017-07-04 06:39:18 +03:00
"strings"
2017-11-06 13:58:06 +03:00
"syscall"
"time"
2017-07-04 06:39:18 +03:00
2017-10-29 00:58:59 +03:00
"github.com/tidwall/evio"
2017-07-04 06:39:18 +03:00
"github.com/tidwall/redcon"
)
2017-10-28 22:23:13 +03:00
type conn struct {
2017-10-29 00:58:59 +03:00
is evio.InputStream
2017-10-28 22:23:13 +03:00
addr string
2017-11-06 13:58:06 +03:00
wget bool
}
func Dial(network, addr string) (fd int, err error) {
taddr, err := net.ResolveTCPAddr(network, addr)
if err != nil {
return 0, err
}
fd, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
if err != nil {
return 0, err
}
if err := syscall.SetNonblock(fd, true); err != nil {
syscall.Close(fd)
return 0, err
}
var sa syscall.SockaddrInet4
copy(sa.Addr[:], taddr.IP[:])
sa.Port = taddr.Port
err = syscall.Connect(fd, &sa)
if err != nil && err != syscall.EINPROGRESS {
syscall.Close(fd)
return 0, err
}
return fd, nil
2017-10-28 22:23:13 +03:00
}
2017-07-04 06:39:18 +03:00
func main() {
2017-11-02 23:29:13 +03:00
var port int
var unixsocket string
2017-11-06 13:58:06 +03:00
var ctx evio.Context
2017-11-02 23:29:13 +03:00
flag.IntVar(&port, "port", 6380, "server port")
flag.StringVar(&unixsocket, "unixsocket", "socket", "unix socket")
flag.Parse()
2017-10-28 22:23:13 +03:00
var conns = make(map[int]*conn)
2017-07-04 06:39:18 +03:00
var keys = make(map[string]string)
2017-10-29 00:58:59 +03:00
var events evio.Events
2017-11-06 13:58:06 +03:00
events.Serving = func(ctxin evio.Context) (action evio.Action) {
ctx = ctxin
2017-11-02 23:29:13 +03:00
log.Printf("redis server started on port %d", port)
if unixsocket != "" {
log.Printf("redis server started at %s", unixsocket)
}
2017-10-28 03:01:03 +03:00
return
}
2017-11-06 13:58:06 +03:00
events.Attached = func(id int, v interface{}) (out []byte, opts evio.Options, action evio.Action) {
conns[id] = &conn{wget: true}
println("attached", id)
out = []byte("GET / HTTP/1.0\r\n\r\n")
return
}
2017-10-30 00:05:23 +03:00
events.Opened = func(id int, addr evio.Addr) (out []byte, opts evio.Options, action evio.Action) {
2017-11-06 13:58:06 +03:00
println("opened", id)
2017-10-28 22:23:13 +03:00
conns[id] = &conn{}
return
}
2017-11-02 03:36:35 +03:00
events.Closed = func(id int, err error) (action evio.Action) {
2017-11-06 13:58:06 +03:00
fmt.Printf("closed %d %v\n", id, err)
2017-10-28 03:01:03 +03:00
delete(conns, id)
return
}
2017-10-29 00:58:59 +03:00
events.Data = func(id int, in []byte) (out []byte, action evio.Action) {
2017-10-28 22:23:13 +03:00
c := conns[id]
2017-11-06 13:58:06 +03:00
if c.wget {
println(string(in))
return
}
2017-10-28 22:23:13 +03:00
data := c.is.Begin(in)
2017-10-28 03:01:03 +03:00
var n int
var complete bool
var err error
var args [][]byte
2017-10-29 00:58:59 +03:00
for action == evio.None {
2017-10-28 03:01:03 +03:00
complete, args, _, data, err = redcon.ReadNextCommand(data, args[:0])
if err != nil {
2017-10-29 00:58:59 +03:00
action = evio.Close
2017-10-28 03:01:03 +03:00
out = redcon.AppendError(out, err.Error())
2017-07-04 06:39:18 +03:00
break
}
2017-10-28 03:01:03 +03:00
if !complete {
break
2017-07-04 06:39:18 +03:00
}
2017-10-28 03:01:03 +03:00
if len(args) > 0 {
n++
2017-07-04 06:39:18 +03:00
switch strings.ToUpper(string(args[0])) {
default:
2017-10-28 03:01:03 +03:00
out = redcon.AppendError(out, "ERR unknown command '"+string(args[0])+"'")
2017-11-06 13:58:06 +03:00
case "WGET":
if len(args) != 2 {
out = redcon.AppendError(out, "ERR wrong number of arguments for '"+string(args[0])+"' command")
} else {
start := time.Now()
fd, err := Dial("tcp", string(args[1]))
if err != nil {
out = redcon.AppendError(out, err.Error())
} else {
time.Since(start)
out = redcon.AppendOK(out)
ctx.Attach(fd)
}
// conn, err := net.Dial("tcp", string(args[1]))
// if err != nil {
// out = redcon.AppendError(out, err.Error())
// } else {
// println(time.Since(start).String())
// f, err := conn.(*net.TCPConn).File()
// if err != nil {
// conn.Close()
// out = redcon.AppendError(out, err.Error())
// } else {
// out = redcon.AppendOK(out)
// ctx.Attach(f.Fd())
// }
// }
}
2017-07-04 06:39:18 +03:00
case "PING":
2017-10-28 22:34:12 +03:00
if len(args) > 2 {
out = redcon.AppendError(out, "ERR wrong number of arguments for '"+string(args[0])+"' command")
} else if len(args) == 2 {
out = redcon.AppendBulk(out, args[1])
} else {
out = redcon.AppendString(out, "PONG")
}
case "ECHO":
if len(args) != 2 {
out = redcon.AppendError(out, "ERR wrong number of arguments for '"+string(args[0])+"' command")
} else {
out = redcon.AppendBulk(out, args[1])
}
2017-07-04 06:39:18 +03:00
case "SHUTDOWN":
2017-10-28 03:01:03 +03:00
out = redcon.AppendString(out, "OK")
2017-10-29 00:58:59 +03:00
action = evio.Shutdown
2017-10-28 03:01:03 +03:00
case "QUIT":
out = redcon.AppendString(out, "OK")
2017-10-29 00:58:59 +03:00
action = evio.Close
2017-07-04 06:39:18 +03:00
case "GET":
if len(args) != 2 {
2017-10-28 03:01:03 +03:00
out = redcon.AppendError(out, "ERR wrong number of arguments for '"+string(args[0])+"' command")
} else {
val, ok := keys[string(args[1])]
if !ok {
out = redcon.AppendNull(out)
} else {
out = redcon.AppendBulkString(out, val)
}
2017-07-04 06:39:18 +03:00
}
2017-10-28 03:01:03 +03:00
case "SET":
if len(args) != 3 {
out = redcon.AppendError(out, "ERR wrong number of arguments for '"+string(args[0])+"' command")
2017-07-04 06:39:18 +03:00
} else {
2017-10-28 03:01:03 +03:00
keys[string(args[1])] = string(args[2])
out = redcon.AppendString(out, "OK")
2017-07-04 06:39:18 +03:00
}
case "DEL":
if len(args) < 2 {
2017-10-28 03:01:03 +03:00
out = redcon.AppendError(out, "ERR wrong number of arguments for '"+string(args[0])+"' command")
} else {
var n int
for i := 1; i < len(args); i++ {
if _, ok := keys[string(args[1])]; ok {
n++
delete(keys, string(args[1]))
}
2017-07-04 06:39:18 +03:00
}
2017-10-28 03:01:03 +03:00
out = redcon.AppendInt(out, int64(n))
2017-07-04 06:39:18 +03:00
}
2017-10-28 22:23:13 +03:00
case "FLUSHDB":
keys = make(map[string]string)
out = redcon.AppendString(out, "OK")
2017-07-04 06:39:18 +03:00
}
}
2017-10-28 03:01:03 +03:00
}
2017-10-28 22:23:13 +03:00
c.is.End(data)
2017-10-28 03:01:03 +03:00
return
}
2017-11-03 04:31:36 +03:00
addrs := []string{fmt.Sprintf("tcp://:%d", port)}
2017-11-02 23:29:13 +03:00
if unixsocket != "" {
addrs = append(addrs, fmt.Sprintf("unix://%s", unixsocket))
}
err := evio.Serve(events, addrs...)
2017-10-28 03:01:03 +03:00
if err != nil {
log.Fatal(err)
}
2017-07-04 06:39:18 +03:00
}