carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package dbreads
2
3import (
4 "encoding/binary"
5 "encoding/json"
6 "errors"
7 "io"
8
9 "github.com/carlosframework/platform/internal/dbdoor"
10)
11
12// ProtocolVersion is the worker protocol's version, answered in hello. A
13// worker that speaks another one never advertises anything (spec §7.1).
14const ProtocolVersion = 1
15
16// Frames are one type byte, a four-byte big-endian length and the payload,
17// at most maxFrame bytes (spec §8.3: length-framed with a fixed maximum).
18const (
19 maxFrame = 1 << 20
20 fileFrameBytes = 256 << 10
21 outputFrameBytes = 64 << 10
22)
23
24const (
25 frameHello byte = 'H' // agent: the readiness probe
26 frameHelloReply byte = 'h' // worker: its version and its view of its sandbox
27 frameHeader byte = 'P' // agent: the plan
28 frameFile byte = 'F' // agent: file bytes, in the header's order
29 frameReady byte = 'R' // worker: restored and checked; runs nothing yet
30 frameRun byte = 'X' // agent: check B passed; the statement
31 frameStop byte = 'S' // agent: end without running
32 frameOutput byte = 'O' // worker: output bytes
33 frameEnd byte = 'E' // worker: how it ended
34)
35
36var (
37 errFrameTooLarge = errors.New("dbreads: frame over the protocol's maximum")
38 errProtocol = errors.New("dbreads: unexpected frame")
39)
40
41type hello struct {
42 V int `json:"v"`
43}
44
45// HelloReply is the worker's answer to the probe: its protocol version and
46// what it can see of the box (spec §7.1). A sandboxed worker sees only the
47// loopback interface, an empty /data and no /etc/carlos.
48type HelloReply struct {
49 V int `json:"v"`
50 Interfaces []string `json:"interfaces"`
51 DataEntries int `json:"data_entries"`
52 EtcReadable bool `json:"etc_readable"`
53 // Errors names every observation the worker could not make. A probe
54 // fails on any: an unobserved sandbox is not a sandbox.
55 Errors []string `json:"errors,omitempty"`
56}
57
58type planHeader struct {
59 V int `json:"v"`
60 Op string `json:"op"`
61 Files []dbdoor.LTXFile `json:"files"`
62 Target uint64 `json:"target"`
63 MaxRestored int64 `json:"max_restored"`
64}
65
66type readyMsg struct {
67 TXID uint64 `json:"txid"`
68}
69
70type runMsg struct {
71 SQL string `json:"sql"`
72}
73
74type endMsg struct {
75 Code string `json:"code"`
76 Error string `json:"error"`
77}
78
79// writeFrame writes one frame in a single Write.
80func writeFrame(w io.Writer, typ byte, p []byte) error {
81 if len(p) > maxFrame {
82 return errFrameTooLarge
83 }
84 buf := make([]byte, 5, 5+len(p))
85 buf[0] = typ
86 binary.BigEndian.PutUint32(buf[1:], uint32(len(p)))
87 _, err := w.Write(append(buf, p...))
88 return err
89}
90
91func writeJSONFrame(w io.Writer, typ byte, v any) error {
92 p, err := json.Marshal(v)
93 if err != nil {
94 return err
95 }
96 return writeFrame(w, typ, p)
97}
98
99func readFrame(r io.Reader) (byte, []byte, error) {
100 var hdr [5]byte
101 if _, err := io.ReadFull(r, hdr[:]); err != nil {
102 return 0, nil, err
103 }
104 n := binary.BigEndian.Uint32(hdr[1:])
105 if n > maxFrame {
106 return 0, nil, errFrameTooLarge
107 }
108 p := make([]byte, n)
109 if _, err := io.ReadFull(r, p); err != nil {
110 return 0, nil, err
111 }
112 return hdr[0], p, nil
113}
114