@aptre/bldr-saucer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +44 -0
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/index.js +106 -0
- package/install.js +95 -0
- package/package.json +48 -0
- package/src/fetch_proto.cpp +379 -0
- package/src/fetch_proto.h +70 -0
- package/src/main.cpp +90 -0
- package/src/pipe_client.cpp +228 -0
- package/src/pipe_client.h +67 -0
- package/src/pipe_connection.h +67 -0
- package/src/scheme_forwarder.cpp +173 -0
- package/src/scheme_forwarder.h +36 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
#include "fetch_proto.h"
|
|
2
|
+
#include <cstring>
|
|
3
|
+
|
|
4
|
+
namespace bldr {
|
|
5
|
+
namespace proto {
|
|
6
|
+
|
|
7
|
+
// Base64 decoding table.
|
|
8
|
+
static constexpr int8_t kBase64Table[256] = {
|
|
9
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
10
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
11
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
|
|
12
|
+
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
|
|
13
|
+
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
|
|
14
|
+
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
|
|
15
|
+
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
|
|
16
|
+
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
|
|
17
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
18
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
19
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
20
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
21
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
22
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
23
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
24
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
std::vector<uint8_t> Base64Decode(const std::string& input) {
|
|
28
|
+
std::vector<uint8_t> out;
|
|
29
|
+
out.reserve(input.size() * 3 / 4);
|
|
30
|
+
uint32_t accum = 0;
|
|
31
|
+
int bits = 0;
|
|
32
|
+
for (char c : input) {
|
|
33
|
+
if (c == '=' || c == '\n' || c == '\r') continue;
|
|
34
|
+
int8_t val = kBase64Table[static_cast<uint8_t>(c)];
|
|
35
|
+
if (val < 0) continue;
|
|
36
|
+
accum = (accum << 6) | static_cast<uint32_t>(val);
|
|
37
|
+
bits += 6;
|
|
38
|
+
if (bits >= 8) {
|
|
39
|
+
bits -= 8;
|
|
40
|
+
out.push_back(static_cast<uint8_t>((accum >> bits) & 0xFF));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Protobuf wire type constants.
|
|
47
|
+
static constexpr uint8_t kVarint = 0;
|
|
48
|
+
static constexpr uint8_t kLengthDelimited = 2;
|
|
49
|
+
|
|
50
|
+
// encodeVarint appends a varint to the buffer.
|
|
51
|
+
static void encodeVarint(std::vector<uint8_t>& buf, uint64_t val) {
|
|
52
|
+
while (val >= 0x80) {
|
|
53
|
+
buf.push_back(static_cast<uint8_t>(val | 0x80));
|
|
54
|
+
val >>= 7;
|
|
55
|
+
}
|
|
56
|
+
buf.push_back(static_cast<uint8_t>(val));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// encodeTag appends a field tag.
|
|
60
|
+
static void encodeTag(std::vector<uint8_t>& buf, uint32_t field, uint8_t wire) {
|
|
61
|
+
encodeVarint(buf, (static_cast<uint64_t>(field) << 3) | wire);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// encodeString appends a length-delimited string field.
|
|
65
|
+
static void encodeString(std::vector<uint8_t>& buf, uint32_t field, const std::string& val) {
|
|
66
|
+
if (val.empty()) return;
|
|
67
|
+
encodeTag(buf, field, kLengthDelimited);
|
|
68
|
+
encodeVarint(buf, val.size());
|
|
69
|
+
buf.insert(buf.end(), val.begin(), val.end());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// encodeBytes appends a length-delimited bytes field.
|
|
73
|
+
static void encodeBytes(std::vector<uint8_t>& buf, uint32_t field, const std::vector<uint8_t>& val) {
|
|
74
|
+
if (val.empty()) return;
|
|
75
|
+
encodeTag(buf, field, kLengthDelimited);
|
|
76
|
+
encodeVarint(buf, val.size());
|
|
77
|
+
buf.insert(buf.end(), val.begin(), val.end());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// encodeBool appends a bool varint field.
|
|
81
|
+
static void encodeBool(std::vector<uint8_t>& buf, uint32_t field, bool val) {
|
|
82
|
+
if (!val) return;
|
|
83
|
+
encodeTag(buf, field, kVarint);
|
|
84
|
+
buf.push_back(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// encodeUint32 appends a uint32 varint field.
|
|
88
|
+
static void encodeUint32(std::vector<uint8_t>& buf, uint32_t field, uint32_t val) {
|
|
89
|
+
if (val == 0) return;
|
|
90
|
+
encodeTag(buf, field, kVarint);
|
|
91
|
+
encodeVarint(buf, val);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// encodeMapEntry encodes a map<string,string> entry as a sub-message.
|
|
95
|
+
// Map entry: key=field 1 (string), value=field 2 (string).
|
|
96
|
+
static void encodeMapEntry(std::vector<uint8_t>& buf, uint32_t field,
|
|
97
|
+
const std::string& key, const std::string& value) {
|
|
98
|
+
// Build the sub-message for the map entry.
|
|
99
|
+
std::vector<uint8_t> entry;
|
|
100
|
+
encodeString(entry, 1, key);
|
|
101
|
+
encodeString(entry, 2, value);
|
|
102
|
+
|
|
103
|
+
// Write as length-delimited sub-message.
|
|
104
|
+
encodeTag(buf, field, kLengthDelimited);
|
|
105
|
+
encodeVarint(buf, entry.size());
|
|
106
|
+
buf.insert(buf.end(), entry.begin(), entry.end());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// encodeLengthDelimitedMsg wraps a sub-message as a field.
|
|
110
|
+
static void encodeLengthDelimitedMsg(std::vector<uint8_t>& buf, uint32_t field,
|
|
111
|
+
const std::vector<uint8_t>& msg) {
|
|
112
|
+
encodeTag(buf, field, kLengthDelimited);
|
|
113
|
+
encodeVarint(buf, msg.size());
|
|
114
|
+
buf.insert(buf.end(), msg.begin(), msg.end());
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// encodeFetchRequestInfo encodes a FetchRequestInfo sub-message.
|
|
118
|
+
static std::vector<uint8_t> encodeFetchRequestInfo(const FetchRequestInfo& info) {
|
|
119
|
+
std::vector<uint8_t> buf;
|
|
120
|
+
encodeString(buf, 1, info.method);
|
|
121
|
+
encodeString(buf, 2, info.url);
|
|
122
|
+
for (const auto& [key, val] : info.headers) {
|
|
123
|
+
encodeMapEntry(buf, 3, key, val);
|
|
124
|
+
}
|
|
125
|
+
encodeBool(buf, 4, info.has_body);
|
|
126
|
+
return buf;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// encodeFetchRequestData encodes a FetchRequestData sub-message.
|
|
130
|
+
static std::vector<uint8_t> encodeFetchRequestData(const FetchRequestData& data) {
|
|
131
|
+
std::vector<uint8_t> buf;
|
|
132
|
+
encodeBytes(buf, 1, data.data);
|
|
133
|
+
encodeBool(buf, 2, data.done);
|
|
134
|
+
return buf;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
std::vector<uint8_t> EncodeFetchRequest_Info(const FetchRequestInfo& info) {
|
|
138
|
+
// FetchRequest: oneof body { request_info = 1; }
|
|
139
|
+
std::vector<uint8_t> buf;
|
|
140
|
+
auto sub = encodeFetchRequestInfo(info);
|
|
141
|
+
encodeLengthDelimitedMsg(buf, 1, sub);
|
|
142
|
+
return buf;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
std::vector<uint8_t> EncodeFetchRequest_Data(const FetchRequestData& data) {
|
|
146
|
+
// FetchRequest: oneof body { request_data = 2; }
|
|
147
|
+
std::vector<uint8_t> buf;
|
|
148
|
+
auto sub = encodeFetchRequestData(data);
|
|
149
|
+
encodeLengthDelimitedMsg(buf, 2, sub);
|
|
150
|
+
return buf;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// decodeVarint reads a varint from buf at offset, updates offset.
|
|
154
|
+
static bool decodeVarint(const uint8_t* buf, size_t len, size_t& offset, uint64_t& val) {
|
|
155
|
+
val = 0;
|
|
156
|
+
int shift = 0;
|
|
157
|
+
while (offset < len) {
|
|
158
|
+
uint8_t b = buf[offset++];
|
|
159
|
+
val |= static_cast<uint64_t>(b & 0x7F) << shift;
|
|
160
|
+
if ((b & 0x80) == 0) return true;
|
|
161
|
+
shift += 7;
|
|
162
|
+
if (shift >= 64) return false;
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// decodeTag reads a field tag and wire type.
|
|
168
|
+
static bool decodeTag(const uint8_t* buf, size_t len, size_t& offset,
|
|
169
|
+
uint32_t& field, uint8_t& wire) {
|
|
170
|
+
uint64_t tag;
|
|
171
|
+
if (!decodeVarint(buf, len, offset, tag)) return false;
|
|
172
|
+
field = static_cast<uint32_t>(tag >> 3);
|
|
173
|
+
wire = static_cast<uint8_t>(tag & 0x07);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// skipField skips over a field value based on wire type.
|
|
178
|
+
static bool skipField(const uint8_t* buf, size_t len, size_t& offset, uint8_t wire) {
|
|
179
|
+
switch (wire) {
|
|
180
|
+
case 0: { // varint
|
|
181
|
+
uint64_t dummy;
|
|
182
|
+
return decodeVarint(buf, len, offset, dummy);
|
|
183
|
+
}
|
|
184
|
+
case 1: // 64-bit
|
|
185
|
+
offset += 8;
|
|
186
|
+
return offset <= len;
|
|
187
|
+
case 2: { // length-delimited
|
|
188
|
+
uint64_t slen;
|
|
189
|
+
if (!decodeVarint(buf, len, offset, slen)) return false;
|
|
190
|
+
offset += slen;
|
|
191
|
+
return offset <= len;
|
|
192
|
+
}
|
|
193
|
+
case 5: // 32-bit
|
|
194
|
+
offset += 4;
|
|
195
|
+
return offset <= len;
|
|
196
|
+
default:
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// decodeLengthDelimited reads a length-delimited field returning a span.
|
|
202
|
+
static bool decodeLengthDelimited(const uint8_t* buf, size_t len, size_t& offset,
|
|
203
|
+
const uint8_t*& data, size_t& dlen) {
|
|
204
|
+
uint64_t slen;
|
|
205
|
+
if (!decodeVarint(buf, len, offset, slen)) return false;
|
|
206
|
+
if (offset + slen > len) return false;
|
|
207
|
+
data = buf + offset;
|
|
208
|
+
dlen = static_cast<size_t>(slen);
|
|
209
|
+
offset += slen;
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// decodeString reads a string from a length-delimited field.
|
|
214
|
+
static bool decodeString(const uint8_t* buf, size_t len, size_t& offset, std::string& out) {
|
|
215
|
+
const uint8_t* data;
|
|
216
|
+
size_t dlen;
|
|
217
|
+
if (!decodeLengthDelimited(buf, len, offset, data, dlen)) return false;
|
|
218
|
+
out.assign(reinterpret_cast<const char*>(data), dlen);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// decodeResponseInfo decodes a ResponseInfo sub-message.
|
|
223
|
+
static bool decodeResponseInfo(const uint8_t* buf, size_t len, ResponseInfo& out) {
|
|
224
|
+
size_t offset = 0;
|
|
225
|
+
while (offset < len) {
|
|
226
|
+
uint32_t field;
|
|
227
|
+
uint8_t wire;
|
|
228
|
+
if (!decodeTag(buf, len, offset, field, wire)) return false;
|
|
229
|
+
|
|
230
|
+
switch (field) {
|
|
231
|
+
case 1: { // headers map
|
|
232
|
+
if (wire != kLengthDelimited) return false;
|
|
233
|
+
const uint8_t* entry;
|
|
234
|
+
size_t elen;
|
|
235
|
+
if (!decodeLengthDelimited(buf, len, offset, entry, elen)) return false;
|
|
236
|
+
// Decode map entry sub-message.
|
|
237
|
+
size_t eoff = 0;
|
|
238
|
+
std::string key, val;
|
|
239
|
+
while (eoff < elen) {
|
|
240
|
+
uint32_t ef;
|
|
241
|
+
uint8_t ew;
|
|
242
|
+
if (!decodeTag(entry, elen, eoff, ef, ew)) return false;
|
|
243
|
+
if (ef == 1 && ew == kLengthDelimited) {
|
|
244
|
+
if (!decodeString(entry, elen, eoff, key)) return false;
|
|
245
|
+
} else if (ef == 2 && ew == kLengthDelimited) {
|
|
246
|
+
if (!decodeString(entry, elen, eoff, val)) return false;
|
|
247
|
+
} else {
|
|
248
|
+
if (!skipField(entry, elen, eoff, ew)) return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (!key.empty()) out.headers[key] = val;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
case 2: { // ok
|
|
255
|
+
if (wire != kVarint) return false;
|
|
256
|
+
uint64_t v;
|
|
257
|
+
if (!decodeVarint(buf, len, offset, v)) return false;
|
|
258
|
+
out.ok = (v != 0);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
case 4: { // status
|
|
262
|
+
if (wire != kVarint) return false;
|
|
263
|
+
uint64_t v;
|
|
264
|
+
if (!decodeVarint(buf, len, offset, v)) return false;
|
|
265
|
+
out.status = static_cast<uint32_t>(v);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case 5: { // status_text
|
|
269
|
+
if (wire != kLengthDelimited) return false;
|
|
270
|
+
if (!decodeString(buf, len, offset, out.status_text)) return false;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
default:
|
|
274
|
+
if (!skipField(buf, len, offset, wire)) return false;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// decodeResponseData decodes a ResponseData sub-message.
|
|
282
|
+
static bool decodeResponseData(const uint8_t* buf, size_t len, ResponseData& out) {
|
|
283
|
+
size_t offset = 0;
|
|
284
|
+
while (offset < len) {
|
|
285
|
+
uint32_t field;
|
|
286
|
+
uint8_t wire;
|
|
287
|
+
if (!decodeTag(buf, len, offset, field, wire)) return false;
|
|
288
|
+
|
|
289
|
+
switch (field) {
|
|
290
|
+
case 1: { // data
|
|
291
|
+
if (wire != kLengthDelimited) return false;
|
|
292
|
+
const uint8_t* data;
|
|
293
|
+
size_t dlen;
|
|
294
|
+
if (!decodeLengthDelimited(buf, len, offset, data, dlen)) return false;
|
|
295
|
+
out.data.assign(data, data + dlen);
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
case 2: { // done
|
|
299
|
+
if (wire != kVarint) return false;
|
|
300
|
+
uint64_t v;
|
|
301
|
+
if (!decodeVarint(buf, len, offset, v)) return false;
|
|
302
|
+
out.done = (v != 0);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
default:
|
|
306
|
+
if (!skipField(buf, len, offset, wire)) return false;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
bool DecodeFetchResponse(const uint8_t* buf, size_t len, FetchResponse& out) {
|
|
314
|
+
// FetchResponse: oneof body { response_info = 1; response_data = 2; }
|
|
315
|
+
size_t offset = 0;
|
|
316
|
+
while (offset < len) {
|
|
317
|
+
uint32_t field;
|
|
318
|
+
uint8_t wire;
|
|
319
|
+
if (!decodeTag(buf, len, offset, field, wire)) return false;
|
|
320
|
+
|
|
321
|
+
switch (field) {
|
|
322
|
+
case 1: { // response_info
|
|
323
|
+
if (wire != kLengthDelimited) return false;
|
|
324
|
+
const uint8_t* sub;
|
|
325
|
+
size_t slen;
|
|
326
|
+
if (!decodeLengthDelimited(buf, len, offset, sub, slen)) return false;
|
|
327
|
+
out.has_info = true;
|
|
328
|
+
if (!decodeResponseInfo(sub, slen, out.info)) return false;
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
case 2: { // response_data
|
|
332
|
+
if (wire != kLengthDelimited) return false;
|
|
333
|
+
const uint8_t* sub;
|
|
334
|
+
size_t slen;
|
|
335
|
+
if (!decodeLengthDelimited(buf, len, offset, sub, slen)) return false;
|
|
336
|
+
out.has_data = true;
|
|
337
|
+
if (!decodeResponseData(sub, slen, out.data)) return false;
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
default:
|
|
341
|
+
if (!skipField(buf, len, offset, wire)) return false;
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
bool DecodeSaucerInit(const uint8_t* buf, size_t len, SaucerInit& out) {
|
|
349
|
+
size_t offset = 0;
|
|
350
|
+
while (offset < len) {
|
|
351
|
+
uint32_t field;
|
|
352
|
+
uint8_t wire;
|
|
353
|
+
if (!decodeTag(buf, len, offset, field, wire)) return false;
|
|
354
|
+
|
|
355
|
+
switch (field) {
|
|
356
|
+
case 1: { // dev_tools
|
|
357
|
+
if (wire != kVarint) return false;
|
|
358
|
+
uint64_t v;
|
|
359
|
+
if (!decodeVarint(buf, len, offset, v)) return false;
|
|
360
|
+
out.dev_tools = (v != 0);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
case 2: { // external_links
|
|
364
|
+
if (wire != kVarint) return false;
|
|
365
|
+
uint64_t v;
|
|
366
|
+
if (!decodeVarint(buf, len, offset, v)) return false;
|
|
367
|
+
out.external_links = static_cast<uint32_t>(v);
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
default:
|
|
371
|
+
if (!skipField(buf, len, offset, wire)) return false;
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
} // namespace proto
|
|
379
|
+
} // namespace bldr
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstdint>
|
|
4
|
+
#include <map>
|
|
5
|
+
#include <string>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
namespace bldr {
|
|
9
|
+
namespace proto {
|
|
10
|
+
|
|
11
|
+
// SaucerInit corresponds to saucer.SaucerInit.
|
|
12
|
+
// Passed from Go as base64-encoded protobuf via BLDR_SAUCER_INIT env var.
|
|
13
|
+
struct SaucerInit {
|
|
14
|
+
bool dev_tools = false; // field 1
|
|
15
|
+
uint32_t external_links = 0; // field 2 (enum ExternalLinks)
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// DecodeSaucerInit decodes a SaucerInit protobuf message.
|
|
19
|
+
bool DecodeSaucerInit(const uint8_t* buf, size_t len, SaucerInit& out);
|
|
20
|
+
|
|
21
|
+
// Base64Decode decodes a base64-encoded string.
|
|
22
|
+
std::vector<uint8_t> Base64Decode(const std::string& input);
|
|
23
|
+
|
|
24
|
+
// FetchRequestInfo corresponds to web.fetch.FetchRequestInfo.
|
|
25
|
+
struct FetchRequestInfo {
|
|
26
|
+
std::string method; // field 1
|
|
27
|
+
std::string url; // field 2
|
|
28
|
+
std::map<std::string, std::string> headers; // field 3
|
|
29
|
+
bool has_body = false; // field 4
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// FetchRequestData corresponds to web.fetch.FetchRequestData.
|
|
33
|
+
struct FetchRequestData {
|
|
34
|
+
std::vector<uint8_t> data; // field 1
|
|
35
|
+
bool done = false; // field 2
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// ResponseInfo corresponds to web.fetch.ResponseInfo.
|
|
39
|
+
struct ResponseInfo {
|
|
40
|
+
std::map<std::string, std::string> headers; // field 1
|
|
41
|
+
bool ok = false; // field 2
|
|
42
|
+
uint32_t status = 0; // field 4
|
|
43
|
+
std::string status_text; // field 5
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// ResponseData corresponds to web.fetch.ResponseData.
|
|
47
|
+
struct ResponseData {
|
|
48
|
+
std::vector<uint8_t> data; // field 1
|
|
49
|
+
bool done = false; // field 2
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// FetchResponse holds a decoded FetchResponse.
|
|
53
|
+
struct FetchResponse {
|
|
54
|
+
bool has_info = false;
|
|
55
|
+
ResponseInfo info;
|
|
56
|
+
bool has_data = false;
|
|
57
|
+
ResponseData data;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// EncodeFetchRequest_Info serializes a FetchRequest with request_info (field 1).
|
|
61
|
+
std::vector<uint8_t> EncodeFetchRequest_Info(const FetchRequestInfo& info);
|
|
62
|
+
|
|
63
|
+
// EncodeFetchRequest_Data serializes a FetchRequest with request_data (field 2).
|
|
64
|
+
std::vector<uint8_t> EncodeFetchRequest_Data(const FetchRequestData& data);
|
|
65
|
+
|
|
66
|
+
// DecodeFetchResponse decodes a FetchResponse message.
|
|
67
|
+
bool DecodeFetchResponse(const uint8_t* buf, size_t len, FetchResponse& out);
|
|
68
|
+
|
|
69
|
+
} // namespace proto
|
|
70
|
+
} // namespace bldr
|
package/src/main.cpp
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#include <saucer/smartview.hpp>
|
|
2
|
+
#include "fetch_proto.h"
|
|
3
|
+
#include "pipe_client.h"
|
|
4
|
+
#include "pipe_connection.h"
|
|
5
|
+
#include "scheme_forwarder.h"
|
|
6
|
+
|
|
7
|
+
#include <cstdlib>
|
|
8
|
+
#include <iostream>
|
|
9
|
+
#include <string>
|
|
10
|
+
#include <thread>
|
|
11
|
+
|
|
12
|
+
coco::stray start(saucer::application* app) {
|
|
13
|
+
const char* runtime_id_env = std::getenv("BLDR_RUNTIME_ID");
|
|
14
|
+
if (!runtime_id_env) {
|
|
15
|
+
std::cerr << "BLDR_RUNTIME_ID not set" << std::endl;
|
|
16
|
+
co_return;
|
|
17
|
+
}
|
|
18
|
+
std::string runtime_id = runtime_id_env;
|
|
19
|
+
|
|
20
|
+
bldr::proto::SaucerInit saucer_init;
|
|
21
|
+
const char* init_b64 = std::getenv("BLDR_SAUCER_INIT");
|
|
22
|
+
if (init_b64) {
|
|
23
|
+
auto data = bldr::proto::Base64Decode(init_b64);
|
|
24
|
+
if (!data.empty()) {
|
|
25
|
+
bldr::proto::DecodeSaucerInit(data.data(), data.size(), saucer_init);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Connect to Go via pipesock.
|
|
30
|
+
bldr::PipeClient pipe;
|
|
31
|
+
std::string pipe_path = ".pipe-" + runtime_id;
|
|
32
|
+
if (!pipe.connect(pipe_path)) {
|
|
33
|
+
std::cerr << "Failed to connect to pipe: " << pipe_path << std::endl;
|
|
34
|
+
co_return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create yamux client session over the pipe.
|
|
38
|
+
// C++ is the client (outbound=true), Go is the server (outbound=false).
|
|
39
|
+
auto conn = std::make_unique<bldr::PipeConnection>(pipe);
|
|
40
|
+
yamux::SessionConfig config;
|
|
41
|
+
config.enable_keepalive = false;
|
|
42
|
+
auto session = yamux::Session::Client(std::move(conn), config);
|
|
43
|
+
if (!session) {
|
|
44
|
+
std::cerr << "Failed to create yamux session" << std::endl;
|
|
45
|
+
co_return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Create the scheme forwarder.
|
|
49
|
+
bldr::SchemeForwarder forwarder(session.get());
|
|
50
|
+
|
|
51
|
+
// Register bldr:// scheme BEFORE creating the webview.
|
|
52
|
+
saucer::webview::register_scheme("bldr");
|
|
53
|
+
|
|
54
|
+
auto window = saucer::window::create(app).value();
|
|
55
|
+
auto webview = saucer::smartview::create({.window = window});
|
|
56
|
+
|
|
57
|
+
window->set_title("Bldr");
|
|
58
|
+
window->set_size({1024, 768});
|
|
59
|
+
|
|
60
|
+
// Handle bldr:// scheme: forward all requests to Go over yamux.
|
|
61
|
+
webview->handle_stream_scheme("bldr", [&forwarder](saucer::scheme::request req, saucer::scheme::stream_writer writer) {
|
|
62
|
+
// Forward in background thread to not block the scheme handler.
|
|
63
|
+
std::thread([&forwarder, req = std::move(req), writer = std::move(writer)]() mutable {
|
|
64
|
+
forwarder.forward(req, writer);
|
|
65
|
+
}).detach();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Navigate to the bootstrap HTML served by Go.
|
|
69
|
+
webview->set_url(saucer::url::make({.scheme = "bldr", .host = "localhost", .path = "/"}));
|
|
70
|
+
|
|
71
|
+
if (saucer_init.dev_tools) {
|
|
72
|
+
webview->set_dev_tools(true);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
window->show();
|
|
76
|
+
co_await app->finish();
|
|
77
|
+
|
|
78
|
+
// Cleanup.
|
|
79
|
+
session->Close();
|
|
80
|
+
pipe.close();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
int main() {
|
|
84
|
+
auto app_result = saucer::application::create({.id = "bldr"});
|
|
85
|
+
if (!app_result) {
|
|
86
|
+
std::cerr << "Failed to create application" << std::endl;
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
return app_result->run(start);
|
|
90
|
+
}
|