resp

package
v2.2.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 5, 2017 License: Apache-2.0 Imports: 9 Imported by: 13

README

RESP

Low-level primitives for dealing with RESP (REdis Serialization Protocol), client and server-side.

Server Examples

Reading requests:

package main

import (
  "fmt"
  "strings"

  "github.com/bsm/redeo/resp"
)

func main() {
	cn := strings.NewReader("*1\r\n$4\r\nPING\r\n*2\r\n$4\r\nEcHO\r\n$5\r\nHeLLO\r\n")
	r := resp.NewRequestReader(cn)

	// read command
	cmd, _ := r.ReadCmd(nil)
	fmt.Println(cmd.Name)
	for i := 0; i < cmd.ArgN(); i++ {
		fmt.Println(i, cmd.Arg(i))
	}

	// read command, recycle previous instance
	cmd, _ = r.ReadCmd(cmd)
	fmt.Println(cmd.Name)
	for i := 0; i < cmd.ArgN(); i++ {
		fmt.Println(i, cmd.Arg(i))
	}

}

Writing responses:

package main

import (
  "bytes"
  "fmt"

  "github.com/bsm/redeo/resp"
)

func main() {
	buf := new(bytes.Buffer)
	w := resp.NewResponseWriter(buf)

	// Append OK response
	w.AppendOK()

	// Append a number
	w.AppendInt(33)

	// Append an array
	w.AppendArrayLen(3)
	w.AppendBulkString("Adam")
	w.AppendBulkString("Had'em")
	w.AppendNil()

	// Writer data must be flushed manually
	fmt.Println(buf.Len(), w.Buffered())
	if err := w.Flush(); err != nil {
		panic(err)
	}

	// Once flushed, it will be sent to the underlying writer
	// as a bulk
	fmt.Println(buf.Len(), w.Buffered())
	fmt.Printf("%q\n", buf.String())

}

Client Examples

Reading requests:

package main

import (
  "fmt"
  "net"

  "github.com/bsm/redeo/resp"
)

func main() {
	cn, _ := net.Dial("tcp", "127.0.0.1:6379")
	defer cn.Close()

	// Wrap connection
	w := resp.NewRequestWriter(cn)
	r := resp.NewResponseReader(cn)

	// Write pipeline
	w.WriteCmdString("PING")
	w.WriteCmdString("ECHO", "HEllO")
	w.WriteCmdString("GET", "key")
	w.WriteCmdString("SET", "key", "value")
	w.WriteCmdString("DEL", "key")

	// Flush pipeline
	if err := w.Flush(); err != nil {
		panic(err)
	}

	// Consume responses
	for i := 0; i < 5; i++ {
		t, err := r.PeekType()
		if err != nil {
			return
		}

		switch t {
		case resp.TypeInline:
			s, _ := r.ReadInlineString()
			fmt.Println(s)
		case resp.TypeBulk:
			s, _ := r.ReadBulkString()
			fmt.Println(s)
		case resp.TypeInt:
			n, _ := r.ReadInt()
			fmt.Println(n)
		case resp.TypeNil:
			_ = r.ReadNil()
			fmt.Println(nil)
		default:
			panic("unexpected response type")
		}
	}

}

Documentation

Overview

Package resp implements low-level primitives for dealing with RESP (REdis Serialization Protocol). It provides client and server side readers and writers.

Example (Client)
package main

import (
	"fmt"
	"net"

	"github.com/bsm/redeo/resp"
)

func main() {
	cn, _ := net.Dial("tcp", "127.0.0.1:6379")
	defer cn.Close()

	// Wrap connection
	w := resp.NewRequestWriter(cn)
	r := resp.NewResponseReader(cn)

	// Write pipeline
	w.WriteCmdString("PING")
	w.WriteCmdString("ECHO", "HEllO")
	w.WriteCmdString("GET", "key")
	w.WriteCmdString("SET", "key", "value")
	w.WriteCmdString("DEL", "key")

	// Flush pipeline
	if err := w.Flush(); err != nil {
		panic(err)
	}

	// Consume responses
	for i := 0; i < 5; i++ {
		t, err := r.PeekType()
		if err != nil {
			return
		}

		switch t {
		case resp.TypeInline:
			s, _ := r.ReadInlineString()
			fmt.Println(s)
		case resp.TypeBulk:
			s, _ := r.ReadBulkString()
			fmt.Println(s)
		case resp.TypeInt:
			n, _ := r.ReadInt()
			fmt.Println(n)
		case resp.TypeNil:
			_ = r.ReadNil()
			fmt.Println(nil)
		default:
			panic("unexpected response type")
		}
	}

}
Output:

PONG
HEllO
<nil>
OK
1

Index

Examples

Constants

View Source
const MaxBufferSize = 64 * 1024

MaxBufferSize is the max request/response buffer size

Variables

This section is empty.

Functions

func IsProtocolError

func IsProtocolError(err error) bool

IsProtocolError returns true if the error is a protocol error

Types

type Command

type Command struct {
	// Name refers to the command name
	Name string

	// Args returns arguments
	Args []CommandArgument
	// contains filtered or unexported fields
}

Command instances are parsed by a RequestReader

func NewCommand

func NewCommand(name string, args ...CommandArgument) *Command

NewCommand returns a new command instance; useful for tests

func (*Command) Arg

func (c *Command) Arg(n int) CommandArgument

Arg returns the Nth argument

func (*Command) ArgN

func (c *Command) ArgN() int

ArgN returns the number of arguments

func (*Command) Context

func (c *Command) Context() context.Context

Context returns the context

func (*Command) Reset

func (c *Command) Reset()

Reset discards all data and resets all state

func (*Command) SetContext

func (c *Command) SetContext(ctx context.Context)

SetContext sets the request context.

type CommandArgument

type CommandArgument []byte

CommandArgument is an argument of a command

func (CommandArgument) Bytes

func (c CommandArgument) Bytes() []byte

Bytes returns the argument as bytes

func (CommandArgument) Float

func (c CommandArgument) Float() (float64, error)

Float returns the argument as a float64.

func (CommandArgument) Int

func (c CommandArgument) Int() (int64, error)

Int returns the argument as an int64.

func (CommandArgument) String

func (c CommandArgument) String() string

String returns the argument converted to a string

type CommandStream

type CommandStream struct {
	// Name refers to the command name
	Name string
	// contains filtered or unexported fields
}

CommandStream instances are created by a RequestReader

func (*CommandStream) ArgN

func (c *CommandStream) ArgN() int

ArgN returns the number of arguments

func (*CommandStream) Context

func (c *CommandStream) Context() context.Context

Context returns the context

func (*CommandStream) Discard

func (c *CommandStream) Discard() error

Discard discards the (remaining) arguments

func (*CommandStream) More

func (c *CommandStream) More() bool

More returns true if there are unread arguments

func (*CommandStream) Next

func (c *CommandStream) Next() (io.Reader, error)

Next returns the next argument as an io.Reader

func (*CommandStream) Reset

func (c *CommandStream) Reset()

Reset discards all data and resets all state

func (*CommandStream) SetContext

func (c *CommandStream) SetContext(ctx context.Context)

SetContext sets the request context.

type CustomResponse

type CustomResponse interface {
	// AppendTo must be implemented by custom response types
	AppendTo(w ResponseWriter)
}

CustomResponse values implement custom serialization and can be passed to ResponseWriter.Append.

type NullString

type NullString struct {
	Value string
	Valid bool
}

NullString is a scannable that can deal with nil values

func (*NullString) ScanResponse

func (s *NullString) ScanResponse(t ResponseType, r ResponseReader) error

ScanResponse implements Scannable

type RequestReader

type RequestReader struct {
	// contains filtered or unexported fields
}

RequestReader is used by servers to wrap a client connection and convert requests into commands.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/bsm/redeo/resp"
)

func main() {
	cn := strings.NewReader("*1\r\n$4\r\nPING\r\n*2\r\n$4\r\nEcHO\r\n$5\r\nHeLLO\r\n")
	r := resp.NewRequestReader(cn)

	// read command
	cmd, _ := r.ReadCmd(nil)
	fmt.Println(cmd.Name)
	for i := 0; i < len(cmd.Args); i++ {
		fmt.Println(i, cmd.Args[i])
	}

	// read command, recycle previous instance
	cmd, _ = r.ReadCmd(cmd)
	fmt.Println(cmd.Name)
	for i := 0; i < len(cmd.Args); i++ {
		fmt.Println(i, cmd.Args[i])
	}

}
Output:

PING
EcHO
0 HeLLO

func NewRequestReader

func NewRequestReader(rd io.Reader) *RequestReader

NewRequestReader wraps any reader interface

func (*RequestReader) Buffered

func (r *RequestReader) Buffered() int

Buffered returns the number of unread bytes.

func (*RequestReader) PeekCmd

func (r *RequestReader) PeekCmd() (string, error)

PeekCmd peeks the next command name.

func (*RequestReader) ReadCmd

func (r *RequestReader) ReadCmd(cmd *Command) (*Command, error)

ReadCmd reads the next command. It optionally recycles the cmd passed.

func (*RequestReader) Reset

func (r *RequestReader) Reset(rd io.Reader)

Reset resets the reader to a new reader and recycles internal buffers.

func (*RequestReader) SkipCmd

func (r *RequestReader) SkipCmd() error

SkipCmd skips the next command.

func (*RequestReader) StreamCmd

func (r *RequestReader) StreamCmd(cmd *CommandStream) (*CommandStream, error)

StreamCmd reads the next command as a stream.

type RequestWriter

type RequestWriter struct {
	// contains filtered or unexported fields
}

RequestWriter is used by clients to send commands to servers.

func NewRequestWriter

func NewRequestWriter(wr io.Writer) *RequestWriter

NewRequestWriter wraps any Writer interface

func (*RequestWriter) Buffered

func (w *RequestWriter) Buffered() int

Buffered returns the number of buffered bytes

func (*RequestWriter) CopyBulk

func (w *RequestWriter) CopyBulk(r io.Reader, n int64) error

CopyBulk is a low-level method to copy a large bulk of data directly to the writer. For normal operation, use WriteCmd or WriteCmdString.

func (*RequestWriter) Flush

func (w *RequestWriter) Flush() error

Flush flushes the output buffer. Call this after you have completed your pipeline

func (*RequestWriter) Reset

func (w *RequestWriter) Reset(wr io.Writer)

Reset resets the writer with an new interface

func (*RequestWriter) WriteBulk

func (w *RequestWriter) WriteBulk(b []byte)

WriteBulk is a low-level method to write a bulk. For normal operation, use WriteCmd or WriteCmdString.

func (*RequestWriter) WriteBulkString

func (w *RequestWriter) WriteBulkString(s string)

WriteBulkString is a low-level method to write a bulk. For normal operation, use WriteCmd or WriteCmdString.

func (*RequestWriter) WriteCmd

func (w *RequestWriter) WriteCmd(cmd string, args ...[]byte)

WriteCmd writes a full command as part of a pipeline. To execute the pipeline, you must call Flush.

func (*RequestWriter) WriteCmdString

func (w *RequestWriter) WriteCmdString(cmd string, args ...string)

WriteCmdString writes a full command as part of a pipeline. To execute the pipeline, you must call Flush.

func (*RequestWriter) WriteMultiBulkSize

func (w *RequestWriter) WriteMultiBulkSize(n int) error

WriteMultiBulkSize is a low-level method to write a multibulk size. For normal operation, use WriteCmd or WriteCmdString.

type ResponseParser

type ResponseParser interface {
	// PeekType returns the type of the next response block
	PeekType() (ResponseType, error)
	// ReadNil reads a nil value
	ReadNil() error
	// ReadBulkString reads a bulk and returns a string
	ReadBulkString() (string, error)
	// ReadBulk reads a bulk and returns bytes (optionally appending to a passed p buffer)
	ReadBulk(p []byte) ([]byte, error)
	// StreamBulk parses a bulk responses and returns a streaming reader object
	// Returned responses must be closed.
	StreamBulk() (io.ReadCloser, error)
	// ReadInt reads an int value
	ReadInt() (int64, error)
	// ReadArrayLen reads the array length
	ReadArrayLen() (int, error)
	// ReadError reads an error string
	ReadError() (string, error)
	// ReadInlineString reads a status string
	ReadInlineString() (string, error)
	// Scan scans results into the given values.
	Scan(vv ...interface{}) error
}

ResponseParser is a basic response parser

type ResponseReader

type ResponseReader interface {
	ResponseParser

	// Buffered returns the number of buffered (unread) bytes.
	Buffered() int
	// Reset resets the reader to a new reader and recycles internal buffers.
	Reset(r io.Reader)
}

ResponseReader is used by clients to wrap a server connection and parse responses.

func NewResponseReader

func NewResponseReader(rd io.Reader) ResponseReader

NewResponseReader returns ResponseReader, which wraps any reader interface, but normally a net.Conn.

type ResponseType

type ResponseType uint8

ResponseType represents the reply type

const (
	TypeUnknown ResponseType = iota
	TypeArray
	TypeBulk
	TypeInline
	TypeError
	TypeInt
	TypeNil
)

response type iota

func (ResponseType) String

func (t ResponseType) String() string

String returns the response type description

type ResponseWriter

type ResponseWriter interface {
	io.Writer

	// AppendArrayLen appends an array header to the output buffer.
	AppendArrayLen(n int)
	// AppendBulk appends bulk bytes to the output buffer.
	AppendBulk(p []byte)
	// AppendBulkString appends a bulk string to the output buffer.
	AppendBulkString(s string)
	// AppendInline appends inline bytes to the output buffer.
	AppendInline(p []byte)
	// AppendInlineString appends an inline string to the output buffer.
	AppendInlineString(s string)
	// AppendError appends an error message to the output buffer.
	AppendError(msg string)
	// AppendErrorf appends an error message to the output buffer.
	AppendErrorf(pattern string, args ...interface{})
	// AppendInt appends a numeric response to the output buffer.
	AppendInt(n int64)
	// AppendNil appends a nil-value to the output buffer.
	AppendNil()
	// AppendOK appends "OK" to the output buffer.
	AppendOK()
	// Append automatically serialized given values and appends them to the output buffer.
	// Supported values include:
	//   * nil
	//   * error
	//   * string
	//   * []byte
	//   * bool
	//   * float32, float64
	//   * int, int8, int16, int32, int64
	//   * uint, uint8, uint16, uint32, uint64
	//   * CustomResponse instances
	//   * slices and maps of any of the above
	Append(v interface{}) error
	// CopyBulk copies n bytes from a reader.
	// This call may flush pending buffer to prevent overflows.
	CopyBulk(src io.Reader, n int64) error
	// Buffered returns the number of pending bytes.
	Buffered() int
	// Flush flushes pending buffer.
	Flush() error
	// Reset resets the writer to a new writer and recycles internal buffers.
	Reset(w io.Writer)
}

ResponseWriter is used by servers to wrap a client connection and send protocol-compatible responses in buffered pipelines.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/bsm/redeo/resp"
)

func main() {
	buf := new(bytes.Buffer)
	w := resp.NewResponseWriter(buf)

	// Append OK response
	w.AppendOK()

	// Append a number
	w.AppendInt(33)

	// Append an array
	w.AppendArrayLen(3)
	w.AppendBulkString("Adam")
	w.AppendBulkString("Had'em")
	w.AppendNil()

	// Writer data must be flushed manually
	fmt.Println(buf.Len(), w.Buffered())
	if err := w.Flush(); err != nil {
		panic(err)
	}

	// Once flushed, it will be sent to the underlying writer
	// as a bulk
	fmt.Println(buf.Len(), w.Buffered())
	fmt.Printf("%q\n", buf.String())

}
Output:

0 41
41 0
"+OK\r\n:33\r\n*3\r\n$4\r\nAdam\r\n$6\r\nHad'em\r\n$-1\r\n"

func NewResponseWriter

func NewResponseWriter(wr io.Writer) ResponseWriter

NewResponseWriter wraps any writer interface, but normally a net.Conn.

type Scannable

type Scannable interface {
	// ScanResponse scans theresponse from the reader
	ScanResponse(t ResponseType, r ResponseReader) error
}

Scannable interfaces may implement custom Scan behaviour

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL