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 ¶
- Constants
- Variables
- func DecodeRequest(dec *packstream.Decoder) (any, error)
- func DecodeResponse(dec *packstream.Decoder) (any, error)
- func EncodeRequest(enc *packstream.Encoder, msg any) error
- func EncodeResponse(enc *packstream.Encoder, msg any) error
- type Begin
- type ChunkedReader
- type ChunkedWriter
- type Commit
- type Discard
- type Failure
- type Goodbye
- type Hello
- type Ignored
- type Logoff
- type Logon
- type Pull
- type Record
- type Reset
- type Rollback
- type Route
- type Run
- type Success
- type Version
Examples ¶
Constants ¶
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).
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.
const Magic = uint32(0x6060B017)
Magic is the 4-byte Bolt protocol preamble sent by the client at the start of every connection.
Variables ¶
var ErrBadMagic = errors.New("bolt: bad magic preamble")
ErrBadMagic is returned by Negotiate when the client sends an incorrect magic preamble.
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.
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.
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 Discard ¶
Discard discards pending records without streaming them to the client. Fields: [extra map {n: int64, qid: int64}].
type Failure ¶
Failure indicates that the preceding request failed. Fields: [metadata map {code string, message string}].
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 ¶
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 ¶
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.