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-07 16:49:33 +03:00
|
|
|
"strconv"
|
2017-07-04 06:39:18 +03:00
|
|
|
"strings"
|
2017-11-06 13:58:06 +03:00
|
|
|
"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
|
|
|
|
}
|
|
|
|
|
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-07 16:49:33 +03:00
|
|
|
var srv evio.Server
|
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-07 16:49:33 +03:00
|
|
|
events.Serving = func(srvin evio.Server) (action evio.Action) {
|
|
|
|
srv = srvin
|
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-07 16:49:33 +03:00
|
|
|
wgetids := make(map[int]time.Time)
|
2017-11-07 19:52:16 +03:00
|
|
|
events.Opened = func(id int, info evio.Info) (out []byte, opts evio.Options, action evio.Action) {
|
2017-11-07 16:49:33 +03:00
|
|
|
c := &conn{}
|
|
|
|
if !wgetids[id].IsZero() {
|
|
|
|
delete(wgetids, id)
|
|
|
|
c.wget = true
|
|
|
|
}
|
|
|
|
conns[id] = c
|
|
|
|
println("opened", id, c.wget)
|
|
|
|
if c.wget {
|
|
|
|
out = []byte("GET / HTTP/1.0\r\n\r\n")
|
|
|
|
}
|
2017-11-06 13:58:06 +03:00
|
|
|
return
|
|
|
|
}
|
2017-11-07 16:49:33 +03:00
|
|
|
events.Tick = func() (delay time.Duration, action evio.Action) {
|
|
|
|
now := time.Now()
|
|
|
|
for id, t := range wgetids {
|
|
|
|
if now.Sub(t) > time.Second {
|
|
|
|
srv.Wake(id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delay = time.Second
|
2017-10-28 22:23:13 +03:00
|
|
|
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 {
|
2017-11-07 19:52:16 +03:00
|
|
|
print(string(in))
|
2017-11-06 13:58:06 +03:00
|
|
|
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":
|
2017-11-07 16:49:33 +03:00
|
|
|
if len(args) != 3 {
|
2017-11-06 13:58:06 +03:00
|
|
|
out = redcon.AppendError(out, "ERR wrong number of arguments for '"+string(args[0])+"' command")
|
|
|
|
} else {
|
2017-11-07 16:49:33 +03:00
|
|
|
n, _ := strconv.ParseInt(string(args[2]), 10, 63)
|
2017-11-08 01:59:24 +03:00
|
|
|
cid := srv.Dial("tcp://"+string(args[1]), time.Duration(n)*time.Second)
|
|
|
|
if cid == 0 {
|
2017-11-07 19:52:16 +03:00
|
|
|
out = redcon.AppendError(out, "failed to dial")
|
2017-11-06 13:58:06 +03:00
|
|
|
} else {
|
2017-11-07 19:52:16 +03:00
|
|
|
wgetids[cid] = time.Now()
|
2017-11-06 13:58:06 +03:00
|
|
|
out = redcon.AppendOK(out)
|
|
|
|
}
|
|
|
|
}
|
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-08 01:59:24 +03:00
|
|
|
addrs := []string{fmt.Sprintf("tcp-net://:%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
|
|
|
}
|