carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "io"
9 "net/http"
10 "os"
11 "strings"
12
13 "github.com/superfly/ltx"
14
15 "github.com/carlosframework/platform/internal/dbdoor"
16)
17
18// replicaDoer serves the db door's two tails (ListTail, GetTail) from rd in
19// process, so the console's own --from restore runs through
20// dbdoor.RestoreToTarget: its retry, its private 0700 directory, its
21// CheckDB, the same code carlos db pull runs (seeded-instances spec §4
22// step 6). The listing is the door's own (listReplicaLevel).
23//
24// It answers the status codes the client reads and no sentences: a tail
25// that is not a coordinate, or a file that is not there, is a 404 (the
26// client's os.ErrNotExist), and a range the door would refuse is a 416.
27// A failed bucket read is returned as the error it is.
28func replicaDoer(rd replicaReader) dbdoor.Doer {
29 return func(ctx context.Context, tail string, h http.Header) (*http.Response, error) {
30 rest, ok := strings.CutPrefix(tail, "/ltx/")
31 if !ok {
32 return doerResponse(http.StatusNotFound, nil), nil
33 }
34 parts := strings.Split(rest, "/")
35 level, ok := parseDBLevel(parts[0])
36 if !ok || len(parts) > 2 {
37 return doerResponse(http.StatusNotFound, nil), nil
38 }
39 if len(parts) == 1 {
40 out, err := listReplicaLevel(ctx, rd, level)
41 if err != nil {
42 return nil, err
43 }
44 body, err := json.Marshal(out)
45 if err != nil {
46 return nil, err
47 }
48 return doerResponse(http.StatusOK, io.NopCloser(bytes.NewReader(body))), nil
49 }
50 lo, hi, err := dbdoor.ParseSpan(parts[1])
51 if err != nil {
52 return doerResponse(http.StatusNotFound, nil), nil
53 }
54 off, size, err := dbdoor.ParseRange(h.Get("Range"))
55 if err != nil {
56 return doerResponse(http.StatusRequestedRangeNotSatisfiable, nil), nil
57 }
58 rc, err := rd.OpenLTXFile(ctx, level, ltx.TXID(lo), ltx.TXID(hi), off, size)
59 if errors.Is(err, os.ErrNotExist) {
60 return doerResponse(http.StatusNotFound, nil), nil
61 }
62 if err != nil {
63 return nil, err
64 }
65 code := http.StatusOK
66 if h.Get("Range") != "" {
67 code = http.StatusPartialContent
68 }
69 return doerResponse(code, rc), nil
70 }
71}
72
73// doerResponse is one in-process answer. A nil body is an empty one, so
74// the client can always close what it is handed.
75func doerResponse(code int, body io.ReadCloser) *http.Response {
76 if body == nil {
77 body = io.NopCloser(strings.NewReader(""))
78 }
79 return &http.Response{StatusCode: code, Header: http.Header{}, Body: body}
80}
81