proto

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package proto implements the Bolt v5 wire protocol message types, handshake negotiation, and chunked framing.

Concurrency: all types in this package are NOT safe for concurrent use unless documented otherwise. Callers must ensure that a single connection's read and write paths are each accessed by at most one goroutine at a time.

Index

Examples

Constants

View Source
const (
	// Client → Server.
	TagHello    byte = 0x01
	TagLogon    byte = 0x6A
	TagLogoff   byte = 0x6B
	TagGoodbye  byte = 0x02
	TagReset    byte = 0x0F
	TagRun      byte = 0x10
	TagPull     byte = 0x3F
	TagDiscard  byte = 0x2F
	TagBegin    byte = 0x11
	TagCommit   byte = 0x12
	TagRollback byte = 0x13
	TagRoute    byte = 0x66

	// Server → Client.
	TagSuccess byte = 0x70
	TagFailure byte = 0x7F
	TagIgnored byte = 0x7E
	TagRecord  byte = 0x71
)

Structure tag bytes for Bolt v5 messages (PackStream Struct tags).

View Source
const DefaultMaxMessageBytes = 16 << 20 // 16 MiB

DefaultMaxMessageBytes is the default upper bound on the cumulative payload size of a single reassembled Bolt message. Chosen so that a Bolt message comfortably accommodates the largest realistic record projection (PackStream lists of strings, large maps from APOC-style procedures, multi-megabyte result rows) while keeping a single malicious client from coercing the server into multi-gigabyte allocations by streaming non-zero chunks indefinitely.

View Source
const Magic = uint32(0x6060B017)

Magic is the 4-byte Bolt protocol preamble sent by the client at the start of every connection.

Variables

View Source
var ErrBadMagic = errors.New("bolt: bad magic preamble")

ErrBadMagic is returned by Negotiate when the client sends an incorrect magic preamble.

View Source
var ErrMessageTooLarge = errors.New("bolt chunk: cumulative message size exceeds MaxMessageBytes")

ErrMessageTooLarge is returned by ChunkedReader.ReadMessage when the cumulative payload size of a single Bolt message would exceed the reader's MaxMessageBytes cap. Inspect with errors.Is. No partially read message is returned; the underlying reader is left positioned at the next byte after the offending chunk's payload so the caller may close the connection cleanly.

View Source
var ErrNoCommonVersion = errors.New("bolt: no common protocol version")

ErrNoCommonVersion is returned by Negotiate when the client's offered versions do not include any version that this server supports.

View Source
var SupportedVersions = []Version{
	{5, 6}, {5, 5}, {5, 4}, {5, 3}, {5, 2}, {5, 1}, {5, 0},
	{4, 4},
}

SupportedVersions is the list of Bolt versions advertised by this server, ordered from highest to lowest preference.

Functions

func DecodeRequest

func DecodeRequest(dec *packstream.Decoder) (any, error)

DecodeRequest reads one complete Bolt request message from dec and returns a typed pointer (*Hello, *Logon, etc.).

func DecodeResponse

func DecodeResponse(dec *packstream.Decoder) (any, error)

DecodeResponse reads one complete Bolt response message from dec and returns a typed pointer (*Success, *Failure, *Ignored, *Record).

func EncodeRequest

func EncodeRequest(enc *packstream.Encoder, msg any) error

EncodeRequest encodes a client→server message msg into enc. msg must be one of: *Hello, *Logon, *Logoff, *Goodbye, *Reset, *Run, *Pull, *Discard, *Begin, *Commit, *Rollback, *Route.

Example

ExampleEncodeRequest round-trips a RUN request. EncodeRequest serialises the message to PackStream; DecodeRequest reconstructs the concrete *proto.Run, preserving the query text and parameters.

package main

import (
	"bytes"
	"fmt"

	"github.com/FlavioCFOliveira/GoGraph/bolt/packstream"
	"github.com/FlavioCFOliveira/GoGraph/bolt/proto"
)

func main() {
	msg := &proto.Run{
		Query:      "MATCH (n) RETURN n",
		Parameters: map[string]packstream.Value{"limit": int64(10)},
	}

	var buf bytes.Buffer
	enc := packstream.NewEncoder(&buf)
	if err := proto.EncodeRequest(enc, msg); err != nil {
		fmt.Println("encode:", err)
		return
	}
	if err := enc.Flush(); err != nil {
		fmt.Println("flush:", err)
		return
	}

	dec := packstream.NewDecoder(&buf)
	decoded, err := proto.DecodeRequest(dec)
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	run := decoded.(*proto.Run)
	fmt.Println("query:", run.Query)
	fmt.Println("limit:", run.Parameters["limit"])
}
Output:
query: MATCH (n) RETURN n
limit: 10

func EncodeResponse

func EncodeResponse(enc *packstream.Encoder, msg any) error

EncodeResponse encodes a server→client message msg into enc. msg must be one of: *Success, *Failure, *Ignored, *Record.

Example

ExampleEncodeResponse round-trips a SUCCESS response carrying server metadata. The decoded *proto.Success exposes the same metadata map.

package main

import (
	"bytes"
	"fmt"

	"github.com/FlavioCFOliveira/GoGraph/bolt/packstream"
	"github.com/FlavioCFOliveira/GoGraph/bolt/proto"
)

func main() {
	msg := &proto.Success{
		Metadata: map[string]packstream.Value{"server": "GoGraph/1.0"},
	}

	var buf bytes.Buffer
	enc := packstream.NewEncoder(&buf)
	if err := proto.EncodeResponse(enc, msg); err != nil {
		fmt.Println("encode:", err)
		return
	}
	if err := enc.Flush(); err != nil {
		fmt.Println("flush:", err)
		return
	}

	dec := packstream.NewDecoder(&buf)
	decoded, err := proto.DecodeResponse(dec)
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	success := decoded.(*proto.Success)
	fmt.Printf("server: %v\n", success.Metadata["server"])
}
Output:
server: GoGraph/1.0

Types

type Begin

type Begin struct {
	Extra map[string]packstream.Value
}

Begin starts an explicit transaction. Fields: [extra map].

type ChunkedReader

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

ChunkedReader reassembles a complete Bolt message from a sequence of length-prefixed chunks read from an underlying buffered reader.

Wire format (per chunk):

uint16 big-endian length  (0 = end-of-message sentinel)
<length bytes of payload>

Bounded growth: every ChunkedReader carries a maxMessageBytes cap (configured via NewChunkedReaderWithLimit; defaults to DefaultMaxMessageBytes for NewChunkedReader). When the cumulative payload of a single message would exceed the cap, ReadMessage returns ErrMessageTooLarge before performing the would-be-oversized allocation. This closes the Slowloris-style DoS vector where a single client streams non-zero chunks until the server OOMs.

ChunkedReader is NOT safe for concurrent use.

func NewChunkedReader

func NewChunkedReader(r io.Reader) *ChunkedReader

NewChunkedReader returns a ChunkedReader that reads from r with the DefaultMaxMessageBytes cap on cumulative message size. Use NewChunkedReaderWithLimit to set a different cap.

func NewChunkedReaderWithLimit

func NewChunkedReaderWithLimit(r io.Reader, maxMessageBytes int) *ChunkedReader

NewChunkedReaderWithLimit returns a ChunkedReader whose ReadMessage rejects any single Bolt message whose cumulative payload size would exceed maxMessageBytes with ErrMessageTooLarge.

A maxMessageBytes value of 0 or negative is replaced with DefaultMaxMessageBytes; the cap can never be disabled by an accidental zero-value configuration. Callers that genuinely want a very large bound should pass it explicitly.

func (*ChunkedReader) ReadMessage

func (cr *ChunkedReader) ReadMessage() ([]byte, error)

ReadMessage reads and reassembles one complete Bolt message.

It reads chunks until it encounters a uint16(0) sentinel, appending each chunk's payload into a contiguous byte slice. The returned slice is freshly allocated and owned by the caller.

Returns io.EOF when the underlying connection is closed cleanly before any bytes of the next message have arrived. Any other I/O error is wrapped and returned.

Returns ErrMessageTooLarge when the cumulative payload of the message in flight would exceed the reader's MaxMessageBytes cap. The check is performed against the prospective total (current msg length + incoming chunkLen) before the would-be-oversized allocation is attempted, so a malicious client cannot coerce a single multi-gigabyte allocation by streaming non-zero chunks indefinitely.

type ChunkedWriter

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

ChunkedWriter frames a logical Bolt message into one or more chunks and writes them to the underlying buffered writer, followed by the uint16(0) end-of-message sentinel. It then flushes the buffer.

ChunkedWriter is NOT safe for concurrent use.

func NewChunkedWriter

func NewChunkedWriter(w io.Writer) *ChunkedWriter

NewChunkedWriter returns a ChunkedWriter that writes to w.

func (*ChunkedWriter) WriteMessage

func (cw *ChunkedWriter) WriteMessage(msg []byte) error

WriteMessage writes msg as one or more Bolt chunks, appends the uint16(0) sentinel, and flushes the underlying writer.

If msg is empty, WriteMessage writes only the sentinel (a valid, zero-length Bolt message).

type Commit

type Commit struct{}

Commit commits the current explicit transaction. Fields: none.

type Discard

type Discard struct {
	N   int64
	QID int64
}

Discard discards pending records without streaming them to the client. Fields: [extra map {n: int64, qid: int64}].

type Failure

type Failure struct {
	Code    string
	Message string
}

Failure indicates that the preceding request failed. Fields: [metadata map {code string, message string}].

type Goodbye

type Goodbye struct{}

Goodbye signals orderly connection teardown. Fields: none.

type Hello

type Hello struct {
	Extra map[string]packstream.Value
}

Hello is sent by the client to initiate a Bolt connection. Fields: [extra map] containing agent, bolt_agent, scheme, principal, credentials, routing, and other driver metadata.

type Ignored

type Ignored struct{}

Ignored indicates that the preceding request was ignored (e.g., the connection is in a failed state). Fields: none.

type Logoff

type Logoff struct{}

Logoff ends an authenticated session without closing the connection. Fields: none.

type Logon

type Logon struct {
	Auth map[string]packstream.Value
}

Logon sends authentication credentials on an established connection. Fields: [auth map].

type Pull

type Pull struct {
	N   int64
	QID int64
}

Pull requests records from the server. Fields: [extra map {n: int64, qid: int64}]. n=-1 means pull all; qid=-1 means the most recent query.

type Record

type Record struct {
	Data []packstream.Value
}

Record carries one row of result data from a query. Fields: [data list].

type Reset

type Reset struct{}

Reset returns the connection to a clean, ready state, discarding any pending results and rolling back any open transaction. Fields: none.

type Rollback

type Rollback struct{}

Rollback rolls back the current explicit transaction. Fields: none.

type Route

type Route struct {
	Routing   map[string]packstream.Value
	Bookmarks []packstream.Value
	DB        packstream.Value // string or nil
}

Route requests routing table information. Fields: [routing map, bookmarks list, db string|null].

type Run

type Run struct {
	Query      string
	Parameters map[string]packstream.Value
	Extra      map[string]packstream.Value
}

Run submits a Cypher query for execution. Fields: [query string, parameters map, extra map].

type Success

type Success struct {
	Metadata map[string]packstream.Value
}

Success indicates that the preceding request succeeded. Fields: [metadata map].

type Version

type Version struct {
	Major, Minor uint8
}

Version represents a Bolt protocol version.

func Negotiate

func Negotiate(ctx context.Context, conn net.Conn) (Version, error)

Negotiate performs the Bolt handshake on conn.

The client sends a 20-byte payload: 4-byte magic followed by four 4-byte version slots. Each slot is big-endian and laid out as:

[0x00, minor_range, minor, major]

minor_range > 0 means the client accepts versions in the range [minor-minor_range, minor], allowing a range offer in a single slot.

Negotiate selects the highest version from SupportedVersions that falls within any offered range, writes back 4 bytes ([0x00, 0x00, minor, major]), and returns the agreed Version. The context deadline, if set, governs I/O timeouts via conn.SetDeadline.

Returns ErrBadMagic if the magic preamble is wrong, ErrNoCommonVersion if no version matches, or an I/O error if the connection fails mid-handshake.

Jump to

Keyboard shortcuts

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