resp

package module
v0.0.0-...-0440c53 Latest Latest
Warning

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

Go to latest
Published: Feb 16, 2025 License: AGPL-3.0 Imports: 11 Imported by: 0

README

go-resp

go-resp is a fast and simple, barebones RESP (Redis serialization protocol) parser and serializer for Go.

Installing

go get -u github.com/Fusl/go-resp

Usage

resp.NewServer() accepts a io.ReadWriter, io.ReadWriteCloser, or net.Conn and returns a resp.Server which can be used to read and write RESP messages.

Reading

Every Server instance has a Next() method which reads the next RESP message from the connection. The method returns a [][]byte slice containing the RESP message and an error. Care must be taken when reading the message as the slice and the underlying byte slices are reused for every call to Next(). If in doubt, copy the slice and every byte slice it contains to a newly allocated slice and byte slices.

Writing

Every Server instance has various write methods for writing RESP messages to the connection. The methods are named after the RESP message type they write. By default, messages are written in RESP3 compatible format. Calling SetRESP2Compat(true) on a Server instance will make it write messages in RESP2 compatible format instead, even when calling RESP3 methods such as WriteMapBytes() which are not available in RESP2 and therefore converted to arrays.

See the go-resp package documentation for a list of all available write methods and how to use them.

Concurrency

Reading or writing is not thread-safe and should not be done concurrently. You may read with Next() in one goroutine and write with Write() in another goroutine, but you should not call Next() or Write() concurrently.

Example

See example/example.go for a simple example server.

Benchmarks

Benchmark information
Hardware
  • CPU: Intel Core i9-13900KF @ 5.5GHz
  • RAM: 4x 32GB DDR4 @ 3200MHz
Benchmark command
$ redis-benchmark -p <port> -c <clients> -P <numreq> -n 100000000 ping
Standard Redis Server
$ redis-server --port 6379 --appendonly no --save ''
-c -P Throughput latency min p50 p95 p99 max
1 1 161,421 0.004 0.000 0.007 0.007 0.007 0.303
1 1000 6,930,488 0.112 0.104 0.119 0.119 0.119 0.279
10 1000 9,683,355 0.935 0.112 0.935 1.007 1.015 1.679
go-resp example server (GOMAXPROCS=8)
$ GOMAXPROCS=8 go run ./example/example.go
-c -P Throughput Increase latency min p50 p95 p99 max
1 1 169,964 +5.29% 0.004 0.000 0.007 0.007 0.007 0.311
1 1000 11,774,402 +69.89% 0.052 0.048 0.055 0.055 0.055 0.159
10 1000 31,318,508 +223.43% 0.188 0.048 0.183 0.271 0.319 0.567
go-resp example server (GOMAXPROCS=1)
$ GOMAXPROCS=1 go run ./example/example.go
-c -P Throughput Increase latency min p50 p95 p99 max
1 1 169,330 +4.90% 0.004 0.000 0.007 0.007 0.007 0.271
1 1000 11,970,313 +72.72% 0.052 0.040 0.055 0.055 0.055 0.199
10 1000 22,456,770 +131.91% 0.412 0.040 0.415 0.775 0.791 0.903

Documentation

Index

Constants

View Source
const (
	MaxInlineSize      = 64 * 1024 // 64 KiB
	MaxMultiBulkLength = math.MaxInt32
	MaxBulkLength      = 128 * 1024 * 1024 // 128 MiB
)
View Source
const ReaderBufferSize = 65536
View Source
const WriterBufferSize = 65536

Variables

View Source
var (
	ErrProtoUnbalancedQuotes       = errors.New("Protocol error: unbalanced quotes in request")
	ErrProtoInvalidMultiBulkLength = errors.New("Protocol error: invalid multibulk length")
	ErrProtoInvalidBulkLength      = errors.New("Protocol error: invalid bulk length")
	ErrProtoExpectedString         = errors.New("Protocol error: expected '$'")
)

Functions

func Expand

func Expand[S ~[]E, E any](s S, n int) S

Expand works similarly to slices.Grow but returns the expanded slice rather than the capped slice.

func ParseInt64

func ParseInt64(b []byte) (int64, error)

func ParseUInt32

func ParseUInt32(b []byte) (uint32, error)

func Pointer

func Pointer[T any](v T) *T

func SanitizeSimpleString

func SanitizeSimpleString(buf []byte) []byte

SanitizeSimpleString replaces all '\r' and '\n' characters with spaces. Useful when writing untrusted error messages to the client.

Types

type Server

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

func NewServer

func NewServer(rw io.ReadWriter) *Server

NewServer returns a new wrapped incoming connection with sane default parameters set.

func (*Server) Close

func (s *Server) Close() error

Close gracefully closes the incoming connection after flushing any pending writes.

func (*Server) CloseWithError

func (s *Server) CloseWithError(err error) error

CloseWithError closes the incoming connection after writing an error response. This is a convenience function that combines WriteError and Close.

func (*Server) Next

func (s *Server) Next() ([][]byte, error)

Next reads the next command from the incoming connection and returns it as a slice of byte slices. The returned slice is only valid until the next call to Next as it is reused for each command. If in doubt, copy the slice and every byte slice it contains to a newly allocated slice and byte slices.

func (*Server) Reset

func (s *Server) Reset(rw io.ReadWriter)

Reset starts reading and writing commands from the newly passed reader and writer. Mostly used for testing purposes.

func (*Server) ResetReader

func (s *Server) ResetReader(r io.Reader)

ResetReader starts reading commands from the newly passed reader. Mostly used for testing purposes.

func (*Server) SetOptions

func (s *Server) SetOptions(opts ServerOptions) error

SetOptions sets the options for the incoming connection.

func (*Server) SetRESP2Compat

func (s *Server) SetRESP2Compat(v bool)

SetRESP2Compat sets the RESP2 compatibility mode of the incoming connection. This mode is useful when talking with a RESP2 client that does not support the new RESP3 types. In this mode, Write functions will convert RESP3 types to RESP2 types where possible.

func (*Server) Write

func (s *Server) Write(v any) error

Write writes any value that can be converted to a RESP type.

func (*Server) WriteArray

func (s *Server) WriteArray(v []any) error

WriteArray writes an array of any values that can be converted to a RESP type.

func (*Server) WriteArrayBytes

func (s *Server) WriteArrayBytes(v [][]byte) error

WriteArrayBytes is a convenience method to write an array of Bulk strings.

func (*Server) WriteArrayHeader

func (s *Server) WriteArrayHeader(l int) error

WriteArrayHeader writes an [Array](https://redis.io/docs/latest/develop/reference/protocol-spec/#array-reply) header.

func (*Server) WriteArrayString

func (s *Server) WriteArrayString(v []string) error

WriteArrayString is a convenience method to write an array of Bulk strings.

func (*Server) WriteAttr

func (s *Server) WriteAttr(v map[string]any) error

WriteAttr writes an [Attribute](https://github.com/antirez/RESP3/blob/master/spec.md#attribute-type) with the given data. For RESP2 connections, function calls are silently discarded and no data is written

func (*Server) WriteAttrBytes

func (s *Server) WriteAttrBytes(v map[string][]byte) error

WriteAttrBytes writes an [Attribute](https://github.com/antirez/RESP3/blob/master/spec.md#attribute-type) with the given data. For RESP2 connections, function calls are silently discarded and no data is written

func (*Server) WriteAttrString

func (s *Server) WriteAttrString(v map[string]string) error

WriteAttrString writes an [Attribute](https://github.com/antirez/RESP3/blob/master/spec.md#attribute-type) with the given data. For RESP2 connections, function calls are silently discarded and no data is written

func (*Server) WriteBigInt

func (s *Server) WriteBigInt(v big.Int) error

WriteBigInt writes a [Big number](https://redis.io/docs/latest/develop/reference/protocol-spec/#big-number-reply) for RESP3 connections or an [Integer](https://redis.io/docs/latest/develop/reference/protocol-spec/#integer-reply) for RESP2 connections. If v cannot be represented in an int64, the result is undefined when sending to a RESP2 client.

func (*Server) WriteBlobError

func (s *Server) WriteBlobError(e error) error

WriteBlobError writes a [Bulk error](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-error-reply) for RESP3 connections or a sanitized [Simple error](https://redis.io/docs/latest/develop/reference/protocol-spec/#error-reply) for RESP2 connections.

func (*Server) WriteBool

func (s *Server) WriteBool(v bool) error

WriteBool writes a [Boolean](https://redis.io/docs/latest/develop/reference/protocol-spec/#boolean-reply) for RESP3 connections or an [Integer](https://redis.io/docs/latest/develop/reference/protocol-spec/#integer-reply) of `0` or `1` for RESP2 connections.

func (*Server) WriteBytes

func (s *Server) WriteBytes(v []byte) error

WriteBytes writes a [Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-string-reply).

func (*Server) WriteError

func (s *Server) WriteError(e error) error

WriteError writes a sanitized [Simple error](https://redis.io/docs/latest/develop/reference/protocol-spec/#error-reply).

func (*Server) WriteExplicitNullArray

func (s *Server) WriteExplicitNullArray() error

WriteExplicitNullArray writes a [Null Array](https://redis.io/docs/latest/develop/reference/protocol-spec/#nil-array-reply).

func (*Server) WriteExplicitNullString

func (s *Server) WriteExplicitNullString() error

WriteExplicitNullString writes a [Null Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#nil-reply).

func (*Server) WriteFloat

func (s *Server) WriteFloat(v float64) error

WriteFloat writes a [Double](https://redis.io/docs/latest/develop/reference/protocol-spec/#double-reply) for RESP3 connections or a [Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-string-reply) containing the string representation of the float for RESP2 connections.

func (*Server) WriteInt

func (s *Server) WriteInt(v int) error

WriteInt writes an [Integer](https://redis.io/docs/latest/develop/reference/protocol-spec/#integer-reply).

func (*Server) WriteInt64

func (s *Server) WriteInt64(v int64) error

WriteInt64 writes an [Integer](https://redis.io/docs/latest/develop/reference/protocol-spec/#integer-reply).

func (*Server) WriteMap

func (s *Server) WriteMap(v map[string]any) error

WriteMap writes a map of any values that can be converted to a RESP type.

func (*Server) WriteMapBytes

func (s *Server) WriteMapBytes(v map[string][]byte) error

WriteMapBytes is a convenience method to write a map of Bulk strings.

func (*Server) WriteMapHeader

func (s *Server) WriteMapHeader(l int) error

WriteMapHeader writes a [Map](https://redis.io/docs/latest/develop/reference/protocol-spec/#map-reply) header with the specified length. For RESP2 connections, this writes an [Array](https://redis.io/docs/latest/develop/reference/protocol-spec/#array-reply) header with twice the specified length.

func (*Server) WriteMapString

func (s *Server) WriteMapString(v map[string]string) error

WriteMapString is a convenience method to write a map of Bulk strings.

func (*Server) WriteNullArray

func (s *Server) WriteNullArray() error

WriteNullArray writes a [Null](https://redis.io/docs/latest/develop/reference/protocol-spec/#null-reply) for RESP3 connections or a [Null Array](https://redis.io/docs/latest/develop/reference/protocol-spec/#nil-array-reply) for RESP2 connections.

func (*Server) WriteNullString

func (s *Server) WriteNullString() error

WriteNullString writes a [Null](https://redis.io/docs/latest/develop/reference/protocol-spec/#null-reply) for RESP3 connections or a [Null Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#nil-reply) for RESP2 connections.

func (*Server) WriteOK

func (s *Server) WriteOK() error

WriteOK is a convenience method for calling WriteStatusBytes with "OK".

func (*Server) WritePush

func (s *Server) WritePush(v []any) error

WritePushHeader writes a [Push](https://redis.io/docs/latest/develop/reference/protocol-spec/#push-event) event with the given data. For RESP2 connections, function calls are silently discarded and no data is written.

func (*Server) WritePushBytes

func (s *Server) WritePushBytes(v [][]byte) error

WritePushHeader writes a [Push](https://redis.io/docs/latest/develop/reference/protocol-spec/#push-event) event with the given data. For RESP2 connections, function calls are silently discarded and no data is written.

func (*Server) WritePushString

func (s *Server) WritePushString(v []string) error

WritePushHeader writes a [Push](https://redis.io/docs/latest/develop/reference/protocol-spec/#push-event) event with the given data. For RESP2 connections, function calls are silently discarded and no data is written.

func (*Server) WriteSet

func (s *Server) WriteSet(v []any) error

WriteSet writes a set of any values that can be converted to a RESP type.

func (*Server) WriteSetBytes

func (s *Server) WriteSetBytes(v [][]byte) error

WriteSetBytes is a convenience method to write a set of Bulk strings.

func (*Server) WriteSetHeader

func (s *Server) WriteSetHeader(l int) error

WriteSetHeader writes a [Set](https://redis.io/docs/latest/develop/reference/protocol-spec/#set-reply) header with the specified length. For RESP2 connections, this writes an [Array](https://redis.io/docs/latest/develop/reference/protocol-spec/#array-reply) header with twice the specified length.

func (*Server) WriteSetString

func (s *Server) WriteSetString(v []string) error

WriteSetString is a convenience method to write a set of Bulk strings.

func (*Server) WriteStatusBytes

func (s *Server) WriteStatusBytes(v []byte) error

WriteSimpleString writes a [Simple string](https://redis.io/docs/latest/develop/reference/protocol-spec/#simple-string-reply).

func (*Server) WriteStatusString

func (s *Server) WriteStatusString(v string) error

WriteSimpleString writes a [Simple string](https://redis.io/docs/latest/develop/reference/protocol-spec/#simple-string-reply).

func (*Server) WriteString

func (s *Server) WriteString(v string) error

WriteString writes a [Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-string-reply).

func (*Server) WriteVerbatimBytes

func (s *Server) WriteVerbatimBytes(v []byte) error

WriteBlobString writes a [Verbatim string](https://redis.io/docs/latest/develop/reference/protocol-spec/#verbatim-string-reply) for RESP3 connections or a [Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-string-reply) for RESP2 connections. The verbatim string needs to contain the data encoding part and the data itself. Example: `txt:Arbitrary text data`. The data encoding part is stripped when sending the data as Bulk string to RESP2 clients.

func (*Server) WriteVerbatimString

func (s *Server) WriteVerbatimString(v string) error

WriteBlobString writes a [Verbatim string](https://redis.io/docs/latest/develop/reference/protocol-spec/#verbatim-string-reply) for RESP3 connections or a [Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-string-reply) for RESP2 connections. The verbatim string needs to contain the data encoding part and the data itself. Example: `txt:Arbitrary text data`. The data encoding part is stripped when sending the data as Bulk string to RESP2 clients.

type ServerOptions

type ServerOptions struct {
	// MaxMultiBulkLength sets the maximum number of elements in a multi-bulk command request.
	MaxMultiBulkLength *int

	// MaxBulkLength sets the maximum length of a bulk string in bytes.
	MaxBulkLength *int

	// MaxBufferSize sets the maximum size of the buffer used to read full commands from the client.
	MaxBufferSize *int

	// RESP2Compat sets the RESP2 compatibility mode of the incoming connection.
	// This mode is useful when talking with a RESP2 client that does not support the new RESP3 types.
	// In this mode, Write functions will convert RESP3 types to RESP2 types where possible.
	RESP2Compat *bool
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳