mirror of https://github.com/tidwall/tile38.git
optional output formats
This commit is contained in:
parent
4e2bbf1c33
commit
4ce4e1af71
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/peterh/liner"
|
"github.com/peterh/liner"
|
||||||
|
"github.com/tidwall/resp"
|
||||||
"github.com/tidwall/tile38/client"
|
"github.com/tidwall/tile38/client"
|
||||||
"github.com/tidwall/tile38/core"
|
"github.com/tidwall/tile38/core"
|
||||||
)
|
)
|
||||||
|
@ -39,9 +41,11 @@ type connError struct {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hostname = "127.0.0.1"
|
hostname = "127.0.0.1"
|
||||||
|
output = "json"
|
||||||
port = 9851
|
port = 9851
|
||||||
oneCommand string
|
oneCommand string
|
||||||
tokml bool
|
tokml bool
|
||||||
|
raw bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func showHelp() bool {
|
func showHelp() bool {
|
||||||
|
@ -54,8 +58,10 @@ func showHelp() bool {
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stdout, "tile38-cli %s%s\n\n", core.Version, gitsha)
|
fmt.Fprintf(os.Stdout, "tile38-cli %s%s\n\n", core.Version, gitsha)
|
||||||
fmt.Fprintf(os.Stdout, "Usage: tile38-cli [OPTIONS] [cmd [arg [arg ...]]]\n")
|
fmt.Fprintf(os.Stdout, "Usage: tile38-cli [OPTIONS] [cmd [arg [arg ...]]]\n")
|
||||||
fmt.Fprintf(os.Stdout, " -h <hostname> Server hostname (default: %s).\n", hostname)
|
fmt.Fprintf(os.Stdout, " --raw Use raw formatting for replies (default when STDOUT is not a tty)\n")
|
||||||
fmt.Fprintf(os.Stdout, " -p <port> Server port (default: %d).\n", port)
|
fmt.Fprintf(os.Stdout, " --resp Use RESP output formatting (default is JSON output)\n")
|
||||||
|
fmt.Fprintf(os.Stdout, " -h <hostname> Server hostname (default: %s)\n", hostname)
|
||||||
|
fmt.Fprintf(os.Stdout, " -p <port> Server port (default: %d)\n", port)
|
||||||
fmt.Fprintf(os.Stdout, "\n")
|
fmt.Fprintf(os.Stdout, "\n")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -82,9 +88,10 @@ func parseArgs() bool {
|
||||||
fmt.Fprintf(os.Stderr, "Unrecognized option or bad number of args for: '%s'\n", arg)
|
fmt.Fprintf(os.Stderr, "Unrecognized option or bad number of args for: '%s'\n", arg)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for len(args) > 0 {
|
for len(args) > 0 {
|
||||||
arg := readArg("")
|
arg := readArg("")
|
||||||
if arg == "--help" {
|
if arg == "--help" || arg == "-?" {
|
||||||
return showHelp()
|
return showHelp()
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(arg, "-") {
|
if !strings.HasPrefix(arg, "-") {
|
||||||
|
@ -96,6 +103,10 @@ func parseArgs() bool {
|
||||||
return badArg(arg)
|
return badArg(arg)
|
||||||
case "-kml":
|
case "-kml":
|
||||||
tokml = true
|
tokml = true
|
||||||
|
case "--raw":
|
||||||
|
raw = true
|
||||||
|
case "--resp":
|
||||||
|
output = "resp"
|
||||||
case "-h":
|
case "-h":
|
||||||
hostname = readArg(arg)
|
hostname = readArg(arg)
|
||||||
case "-p":
|
case "-p":
|
||||||
|
@ -120,6 +131,15 @@ func main() {
|
||||||
if !parseArgs() {
|
if !parseArgs() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !raw {
|
||||||
|
fi, err := os.Stdout.Stat()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw = (fi.Mode() & os.ModeCharDevice) == 0
|
||||||
|
}
|
||||||
if len(oneCommand) > 0 && (oneCommand[0] == 'h' || oneCommand[0] == 'H') && strings.Split(strings.ToLower(oneCommand), " ")[0] == "help" {
|
if len(oneCommand) > 0 && (oneCommand[0] == 'h' || oneCommand[0] == 'H') && strings.Split(strings.ToLower(oneCommand), " ")[0] == "help" {
|
||||||
showHelp()
|
showHelp()
|
||||||
return
|
return
|
||||||
|
@ -222,7 +242,13 @@ func main() {
|
||||||
f.Close()
|
f.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var raw bool
|
if output == "resp" {
|
||||||
|
_, err := conn.Do("output resp")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
var command string
|
var command string
|
||||||
var err error
|
var err error
|
||||||
|
@ -254,11 +280,6 @@ func main() {
|
||||||
if (command[0] == 'q' || command[0] == 'Q') && strings.ToLower(command) == "quit" {
|
if (command[0] == 'q' || command[0] == 'Q') && strings.ToLower(command) == "quit" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (command[0] == 'r' || command[0] == 'R') && strings.ToLower(command) == "raw" {
|
|
||||||
raw = true
|
|
||||||
fmt.Fprintln(os.Stderr, "raw mode is ON")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (command[0] == 'h' || command[0] == 'H') && (strings.ToLower(command) == "help" || strings.HasPrefix(strings.ToLower(command), "help")) {
|
if (command[0] == 'h' || command[0] == 'H') && (strings.ToLower(command) == "help" || strings.HasPrefix(strings.ToLower(command), "help")) {
|
||||||
err = help(strings.TrimSpace(command[4:]))
|
err = help(strings.TrimSpace(command[4:]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -266,11 +287,6 @@ func main() {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (command[0] == 'p' || command[0] == 'P') && strings.ToLower(command) == "pretty" {
|
|
||||||
raw = false
|
|
||||||
fmt.Fprintln(os.Stderr, "raw mode is OFF")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
aof = (command[0] == 'a' || command[0] == 'A') && strings.HasPrefix(strings.ToLower(command), "aof ")
|
aof = (command[0] == 'a' || command[0] == 'A') && strings.HasPrefix(strings.ToLower(command), "aof ")
|
||||||
msg, err := conn.Do(command)
|
msg, err := conn.Do(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -296,11 +312,18 @@ func main() {
|
||||||
if mustOutput {
|
if mustOutput {
|
||||||
if tokml {
|
if tokml {
|
||||||
msg = convert2kml(msg)
|
msg = convert2kml(msg)
|
||||||
}
|
fmt.Fprintln(os.Stdout, string(msg))
|
||||||
if raw {
|
} else if output == "resp" {
|
||||||
|
if !raw {
|
||||||
|
msg = convert2termresp(msg)
|
||||||
|
}
|
||||||
fmt.Fprintln(os.Stdout, string(msg))
|
fmt.Fprintln(os.Stdout, string(msg))
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintln(os.Stdout, string(msg))
|
if raw {
|
||||||
|
fmt.Fprintln(os.Stdout, string(msg))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stdout, string(msg))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,6 +338,67 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convert2termresp(msg []byte) []byte {
|
||||||
|
rd := resp.NewReader(bytes.NewBuffer(msg))
|
||||||
|
out := ""
|
||||||
|
for {
|
||||||
|
v, _, err := rd.ReadValue()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
out += convert2termrespval(v, 0)
|
||||||
|
}
|
||||||
|
return []byte(strings.TrimSpace(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func convert2termrespval(v resp.Value, spaces int) string {
|
||||||
|
switch v.Type() {
|
||||||
|
default:
|
||||||
|
return v.String()
|
||||||
|
case resp.BulkString:
|
||||||
|
if v.IsNull() {
|
||||||
|
return "(nil)"
|
||||||
|
} else {
|
||||||
|
return "\"" + v.String() + "\""
|
||||||
|
}
|
||||||
|
case resp.Integer:
|
||||||
|
return "(integer) " + v.String()
|
||||||
|
case resp.Error:
|
||||||
|
return "(error) " + v.String()
|
||||||
|
case resp.Array:
|
||||||
|
arr := v.Array()
|
||||||
|
if len(arr) == 0 {
|
||||||
|
return "(empty list or set)"
|
||||||
|
}
|
||||||
|
out := ""
|
||||||
|
nspaces := spaces + numlen(len(arr))
|
||||||
|
for i, v := range arr {
|
||||||
|
if i > 0 {
|
||||||
|
out += strings.Repeat(" ", spaces)
|
||||||
|
}
|
||||||
|
iout := strings.TrimSpace(convert2termrespval(v, nspaces+2))
|
||||||
|
out += fmt.Sprintf("%d) %s\n", i+1, iout)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func numlen(n int) int {
|
||||||
|
l := 1
|
||||||
|
if n < 0 {
|
||||||
|
l++
|
||||||
|
n = n * -1
|
||||||
|
}
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
if n < 10 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l++
|
||||||
|
n = n / 10
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
func convert2kml(msg []byte) []byte {
|
func convert2kml(msg []byte) []byte {
|
||||||
k := NewKML()
|
k := NewKML()
|
||||||
var m map[string]interface{}
|
var m map[string]interface{}
|
||||||
|
|
|
@ -190,7 +190,12 @@ func (c *Controller) handleInputCommand(conn *server.Conn, msg *server.Message,
|
||||||
_, err = io.WriteString(w, res+"\r\n")
|
_, err = io.WriteString(w, res+"\r\n")
|
||||||
return err
|
return err
|
||||||
case server.RESP:
|
case server.RESP:
|
||||||
_, err := io.WriteString(w, res)
|
var err error
|
||||||
|
if msg.OutputType == server.JSON {
|
||||||
|
_, err = fmt.Fprintf(w, "$%d\r\n%s\r\n", len(res), res)
|
||||||
|
} else {
|
||||||
|
_, err = io.WriteString(w, res)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
case server.Native:
|
case server.Native:
|
||||||
_, err := fmt.Fprintf(w, "$%d %s\r\n", len(res), res)
|
_, err := fmt.Fprintf(w, "$%d %s\r\n", len(res), res)
|
||||||
|
@ -278,6 +283,8 @@ func (c *Controller) handleInputCommand(conn *server.Conn, msg *server.Message,
|
||||||
// does not write to aof, but requires a write lock.
|
// does not write to aof, but requires a write lock.
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
case "output":
|
||||||
|
// this is local connection operation. Locks not needed.
|
||||||
case "massinsert":
|
case "massinsert":
|
||||||
// dev operation
|
// dev operation
|
||||||
// ** danger zone **
|
// ** danger zone **
|
||||||
|
@ -340,14 +347,12 @@ func (c *Controller) command(msg *server.Message, w io.Writer) (res string, d co
|
||||||
res, d, err = c.cmdDrop(msg)
|
res, d, err = c.cmdDrop(msg)
|
||||||
case "flushdb":
|
case "flushdb":
|
||||||
res, d, err = c.cmdFlushDB(msg)
|
res, d, err = c.cmdFlushDB(msg)
|
||||||
// case "sethook":
|
case "sethook":
|
||||||
// err = c.cmdSetHook(nline)
|
res, d, err = c.cmdSetHook(msg)
|
||||||
// resp = okResp()
|
case "delhook":
|
||||||
// case "delhook":
|
res, d, err = c.cmdDelHook(msg)
|
||||||
// err = c.cmdDelHook(nline)
|
case "hooks":
|
||||||
// resp = okResp()
|
res, err = c.cmdHooks(msg)
|
||||||
// case "hooks":
|
|
||||||
// err = c.cmdHooks(nline, w)
|
|
||||||
// case "massinsert":
|
// case "massinsert":
|
||||||
// if !core.DevMode {
|
// if !core.DevMode {
|
||||||
// err = fmt.Errorf("unknown command '%s'", cmd)
|
// err = fmt.Errorf("unknown command '%s'", cmd)
|
||||||
|
@ -376,6 +381,8 @@ func (c *Controller) command(msg *server.Message, w io.Writer) (res string, d co
|
||||||
res, err = c.cmdGet(msg)
|
res, err = c.cmdGet(msg)
|
||||||
case "keys":
|
case "keys":
|
||||||
res, err = c.cmdKeys(msg)
|
res, err = c.cmdKeys(msg)
|
||||||
|
case "output":
|
||||||
|
res, err = c.cmdOutput(msg)
|
||||||
// case "aof":
|
// case "aof":
|
||||||
// err = c.cmdAOF(nline, w)
|
// err = c.cmdAOF(nline, w)
|
||||||
// case "aofmd5":
|
// case "aofmd5":
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -42,7 +41,7 @@ type Hook struct {
|
||||||
Key string
|
Key string
|
||||||
Name string
|
Name string
|
||||||
Endpoints []Endpoint
|
Endpoints []Endpoint
|
||||||
Command string
|
Message *server.Message
|
||||||
Fence *liveFenceSwitches
|
Fence *liveFenceSwitches
|
||||||
ScanWriter *scanWriter
|
ScanWriter *scanWriter
|
||||||
}
|
}
|
||||||
|
@ -156,70 +155,96 @@ func parseEndpoint(s string) (Endpoint, error) {
|
||||||
return endpoint, nil
|
return endpoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) cmdSetHook(line string) (err error) {
|
func (c *Controller) cmdSetHook(msg *server.Message) (res string, d commandDetailsT, err error) {
|
||||||
//start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
vs := msg.Values[1:]
|
||||||
var name, values, cmd string
|
var name, values, cmd string
|
||||||
if line, name = token(line); name == "" {
|
var ok bool
|
||||||
return errInvalidNumberOfArguments
|
if vs, name, ok = tokenval(vs); !ok || name == "" {
|
||||||
|
return "", d, errInvalidNumberOfArguments
|
||||||
}
|
}
|
||||||
if line, values = token(line); values == "" {
|
if vs, values, ok = tokenval(vs); !ok || values == "" {
|
||||||
return errInvalidNumberOfArguments
|
return "", d, errInvalidNumberOfArguments
|
||||||
}
|
}
|
||||||
var endpoints []Endpoint
|
var endpoints []Endpoint
|
||||||
for _, value := range strings.Split(values, ",") {
|
for _, value := range strings.Split(values, ",") {
|
||||||
endpoint, err := parseEndpoint(value)
|
endpoint, err := parseEndpoint(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("sethook: %v", err)
|
log.Errorf("sethook: %v", err)
|
||||||
return errInvalidArgument(value)
|
return "", d, errInvalidArgument(value)
|
||||||
}
|
}
|
||||||
endpoints = append(endpoints, endpoint)
|
endpoints = append(endpoints, endpoint)
|
||||||
}
|
}
|
||||||
command := line
|
commandvs := vs
|
||||||
if line, cmd = token(line); cmd == "" {
|
if vs, cmd, ok = tokenval(vs); !ok || cmd == "" {
|
||||||
return errInvalidNumberOfArguments
|
return "", d, errInvalidNumberOfArguments
|
||||||
}
|
}
|
||||||
cmdlc := strings.ToLower(cmd)
|
cmdlc := strings.ToLower(cmd)
|
||||||
var types []string
|
var types []string
|
||||||
switch cmdlc {
|
switch cmdlc {
|
||||||
default:
|
default:
|
||||||
return errInvalidArgument(cmd)
|
return "", d, errInvalidArgument(cmd)
|
||||||
case "nearby":
|
case "nearby":
|
||||||
types = nearbyTypes
|
types = nearbyTypes
|
||||||
case "within", "intersects":
|
case "within", "intersects":
|
||||||
types = withinOrIntersectsTypes
|
types = withinOrIntersectsTypes
|
||||||
}
|
}
|
||||||
var vs []resp.Value
|
|
||||||
panic("todo: assign vs correctly")
|
|
||||||
s, err := c.cmdSearchArgs(cmdlc, vs, types)
|
s, err := c.cmdSearchArgs(cmdlc, vs, types)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", d, err
|
||||||
}
|
}
|
||||||
if !s.fence {
|
if !s.fence {
|
||||||
return errors.New("missing FENCE argument")
|
return "", d, errors.New("missing FENCE argument")
|
||||||
}
|
}
|
||||||
s.cmd = cmdlc
|
s.cmd = cmdlc
|
||||||
|
|
||||||
|
cmsg := &server.Message{}
|
||||||
|
*cmsg = *msg
|
||||||
|
cmsg.Values = commandvs
|
||||||
|
cmsg.Command = strings.ToLower(cmsg.Values[0].String())
|
||||||
|
|
||||||
hook := &Hook{
|
hook := &Hook{
|
||||||
Key: s.key,
|
Key: s.key,
|
||||||
Name: name,
|
Name: name,
|
||||||
Endpoints: endpoints,
|
Endpoints: endpoints,
|
||||||
Fence: &s,
|
Fence: &s,
|
||||||
Command: command,
|
Message: cmsg,
|
||||||
}
|
}
|
||||||
var wr bytes.Buffer
|
var wr bytes.Buffer
|
||||||
var msg *server.Message
|
hook.ScanWriter, err = c.newScanWriter(&wr, cmsg, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
|
||||||
panic("todo: cmdSetHook message must be defined")
|
|
||||||
hook.ScanWriter, err = c.newScanWriter(&wr, msg, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", d, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the previous hook
|
// delete the previous hook
|
||||||
if h, ok := c.hooks[name]; ok {
|
if h, ok := c.hooks[name]; ok {
|
||||||
|
// lets see if the previous hook matches the new hook
|
||||||
|
if h.Key == hook.Key && h.Name == hook.Name {
|
||||||
|
if len(h.Endpoints) == len(hook.Endpoints) {
|
||||||
|
match := true
|
||||||
|
for i, endpoint := range h.Endpoints {
|
||||||
|
if endpoint.Original != hook.Endpoints[i].Original {
|
||||||
|
match = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match && resp.ArrayValue(h.Message.Values).Equals(resp.ArrayValue(hook.Message.Values)) {
|
||||||
|
switch msg.OutputType {
|
||||||
|
case server.JSON:
|
||||||
|
return server.OKMessage(msg, start), d, nil
|
||||||
|
case server.RESP:
|
||||||
|
return ":0\r\n", d, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if hm, ok := c.hookcols[h.Key]; ok {
|
if hm, ok := c.hookcols[h.Key]; ok {
|
||||||
delete(hm, h.Name)
|
delete(hm, h.Name)
|
||||||
}
|
}
|
||||||
delete(c.hooks, h.Name)
|
delete(c.hooks, h.Name)
|
||||||
}
|
}
|
||||||
|
d.updated = true
|
||||||
c.hooks[name] = hook
|
c.hooks[name] = hook
|
||||||
hm, ok := c.hookcols[hook.Key]
|
hm, ok := c.hookcols[hook.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -227,70 +252,121 @@ func (c *Controller) cmdSetHook(line string) (err error) {
|
||||||
c.hookcols[hook.Key] = hm
|
c.hookcols[hook.Key] = hm
|
||||||
}
|
}
|
||||||
hm[name] = hook
|
hm[name] = hook
|
||||||
return nil
|
switch msg.OutputType {
|
||||||
|
case server.JSON:
|
||||||
|
return server.OKMessage(msg, start), d, nil
|
||||||
|
case server.RESP:
|
||||||
|
return ":1\r\n", d, nil
|
||||||
|
}
|
||||||
|
return "", d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) cmdDelHook(line string) (err error) {
|
func (c *Controller) cmdDelHook(msg *server.Message) (res string, d commandDetailsT, err error) {
|
||||||
|
start := time.Now()
|
||||||
|
vs := msg.Values[1:]
|
||||||
|
|
||||||
var name string
|
var name string
|
||||||
if line, name = token(line); name == "" {
|
var ok bool
|
||||||
return errInvalidNumberOfArguments
|
if vs, name, ok = tokenval(vs); !ok || name == "" {
|
||||||
|
return "", d, errInvalidNumberOfArguments
|
||||||
}
|
}
|
||||||
if line != "" {
|
if len(vs) != 0 {
|
||||||
return errInvalidNumberOfArguments
|
return "", d, errInvalidNumberOfArguments
|
||||||
}
|
}
|
||||||
if h, ok := c.hooks[name]; ok {
|
if h, ok := c.hooks[name]; ok {
|
||||||
if hm, ok := c.hookcols[h.Key]; ok {
|
if hm, ok := c.hookcols[h.Key]; ok {
|
||||||
delete(hm, h.Name)
|
delete(hm, h.Name)
|
||||||
}
|
}
|
||||||
delete(c.hooks, h.Name)
|
delete(c.hooks, h.Name)
|
||||||
|
d.updated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch msg.OutputType {
|
||||||
|
case server.JSON:
|
||||||
|
return server.OKMessage(msg, start), d, nil
|
||||||
|
case server.RESP:
|
||||||
|
if d.updated {
|
||||||
|
return ":1\r\n", d, nil
|
||||||
|
} else {
|
||||||
|
return ":0\r\n", d, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) cmdHooks(line string, w io.Writer) (err error) {
|
func (c *Controller) cmdHooks(msg *server.Message) (res string, err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
vs := msg.Values[1:]
|
||||||
|
|
||||||
var pattern string
|
var pattern string
|
||||||
if line, pattern = token(line); pattern == "" {
|
var ok bool
|
||||||
return errInvalidNumberOfArguments
|
if vs, pattern, ok = tokenval(vs); !ok || pattern == "" {
|
||||||
|
return "", errInvalidNumberOfArguments
|
||||||
}
|
}
|
||||||
if line != "" {
|
if len(vs) != 0 {
|
||||||
return errInvalidNumberOfArguments
|
return "", errInvalidNumberOfArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
var hooks []*Hook
|
var hooks []*Hook
|
||||||
for name, hook := range c.hooks {
|
for name, hook := range c.hooks {
|
||||||
if ok, err := globMatch(pattern, name); err == nil && ok {
|
match, _ := globMatch(pattern, name)
|
||||||
|
if match {
|
||||||
hooks = append(hooks, hook)
|
hooks = append(hooks, hook)
|
||||||
} else if err != nil {
|
|
||||||
return errInvalidArgument(pattern)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(hooksByName(hooks))
|
sort.Sort(hooksByName(hooks))
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
switch msg.OutputType {
|
||||||
buf.WriteString(`{"ok":true,"hooks":[`)
|
case server.JSON:
|
||||||
for i, hook := range hooks {
|
buf := &bytes.Buffer{}
|
||||||
if i > 0 {
|
buf.WriteString(`{"ok":true,"hooks":[`)
|
||||||
buf.WriteByte(',')
|
for i, hook := range hooks {
|
||||||
}
|
|
||||||
buf.WriteString(`{`)
|
|
||||||
buf.WriteString(`"name":` + jsonString(hook.Name))
|
|
||||||
buf.WriteString(`,"key":` + jsonString(hook.Key))
|
|
||||||
buf.WriteString(`,"endpoints":[`)
|
|
||||||
for i, endpoint := range hook.Endpoints {
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
buf.WriteByte(',')
|
buf.WriteByte(',')
|
||||||
}
|
}
|
||||||
buf.WriteString(jsonString(endpoint.Original))
|
buf.WriteString(`{`)
|
||||||
}
|
buf.WriteString(`"name":` + jsonString(hook.Name))
|
||||||
buf.WriteString(`],"command":` + jsonString(hook.Command))
|
buf.WriteString(`,"key":` + jsonString(hook.Key))
|
||||||
buf.WriteString(`}`)
|
buf.WriteString(`,"endpoints":[`)
|
||||||
}
|
for i, endpoint := range hook.Endpoints {
|
||||||
buf.WriteString(`],"elapsed":"` + time.Now().Sub(start).String() + "\"}")
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
buf.WriteString(jsonString(endpoint.Original))
|
||||||
|
}
|
||||||
|
buf.WriteString(`],"command":[`)
|
||||||
|
for i, v := range hook.Message.Values {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(`,`)
|
||||||
|
}
|
||||||
|
buf.WriteString(jsonString(v.String()))
|
||||||
|
}
|
||||||
|
|
||||||
w.Write(buf.Bytes())
|
buf.WriteString(`]}`)
|
||||||
return
|
}
|
||||||
|
buf.WriteString(`],"elapsed":"` + time.Now().Sub(start).String() + "\"}")
|
||||||
|
return buf.String(), nil
|
||||||
|
case server.RESP:
|
||||||
|
var vals []resp.Value
|
||||||
|
for _, hook := range hooks {
|
||||||
|
var hvals []resp.Value
|
||||||
|
hvals = append(hvals, resp.StringValue(hook.Name))
|
||||||
|
hvals = append(hvals, resp.StringValue(hook.Key))
|
||||||
|
var evals []resp.Value
|
||||||
|
for _, endpoint := range hook.Endpoints {
|
||||||
|
evals = append(evals, resp.StringValue(endpoint.Original))
|
||||||
|
}
|
||||||
|
hvals = append(hvals, resp.ArrayValue(evals))
|
||||||
|
hvals = append(hvals, resp.ArrayValue(hook.Message.Values))
|
||||||
|
vals = append(vals, resp.ArrayValue(hvals))
|
||||||
|
}
|
||||||
|
data, err := resp.ArrayValue(vals).MarshalRESP()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) sendHTTPMessage(endpoint Endpoint, msg []byte) error {
|
func (c *Controller) sendHTTPMessage(endpoint Endpoint, msg []byte) error {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tidwall/tile38/controller/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) cmdOutput(msg *server.Message) (res string, err error) {
|
||||||
|
start := time.Now()
|
||||||
|
vs := msg.Values[1:]
|
||||||
|
var arg string
|
||||||
|
var ok bool
|
||||||
|
if len(vs) != 0 {
|
||||||
|
if vs, arg, ok = tokenval(vs); !ok || arg == "" {
|
||||||
|
return "", errInvalidNumberOfArguments
|
||||||
|
}
|
||||||
|
// Setting the original message output type will be picked up by the
|
||||||
|
// server prior to the next command being executed.
|
||||||
|
switch strings.ToLower(arg) {
|
||||||
|
default:
|
||||||
|
return "", errInvalidArgument(arg)
|
||||||
|
case "json":
|
||||||
|
msg.OutputType = server.JSON
|
||||||
|
case "resp":
|
||||||
|
msg.OutputType = server.RESP
|
||||||
|
}
|
||||||
|
return server.OKMessage(msg, start), nil
|
||||||
|
}
|
||||||
|
// return the output
|
||||||
|
switch msg.OutputType {
|
||||||
|
default:
|
||||||
|
return "", nil
|
||||||
|
case server.JSON:
|
||||||
|
return `{"ok":true,"output":"json","elapsed":` + time.Now().Sub(start).String() + `}`, nil
|
||||||
|
case server.RESP:
|
||||||
|
return "$4\r\nresp\r\n", nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -98,6 +98,7 @@ func handleConn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
outputType := Null
|
||||||
rd := NewAnyReaderWriter(conn)
|
rd := NewAnyReaderWriter(conn)
|
||||||
brd := rd.rd
|
brd := rd.rd
|
||||||
for {
|
for {
|
||||||
|
@ -114,6 +115,9 @@ func handleConn(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if msg != nil && msg.Command != "" {
|
if msg != nil && msg.Command != "" {
|
||||||
|
if outputType != Null {
|
||||||
|
msg.OutputType = outputType
|
||||||
|
}
|
||||||
if msg.Command == "quit" {
|
if msg.Command == "quit" {
|
||||||
if msg.OutputType == RESP {
|
if msg.OutputType == RESP {
|
||||||
io.WriteString(conn, "+OK\r\n")
|
io.WriteString(conn, "+OK\r\n")
|
||||||
|
@ -125,6 +129,7 @@ func handleConn(
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
outputType = msg.OutputType
|
||||||
} else {
|
} else {
|
||||||
conn.Write([]byte("HTTP/1.1 500 Bad Request\r\nConnection: close\r\n\r\n"))
|
conn.Write([]byte("HTTP/1.1 500 Bad Request\r\nConnection: close\r\n\r\n"))
|
||||||
return
|
return
|
||||||
|
@ -132,6 +137,7 @@ func handleConn(
|
||||||
if msg.ConnType == HTTP || msg.ConnType == WebSocket {
|
if msg.ConnType == HTTP || msg.ConnType == WebSocket {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue