5b9c6ad4 Andy McCall 1 month ago ↶
1
2
750251b2 Andy McCall 1 month ago ↶
3
4
5
6
7
5b9c6ad4 Andy McCall 1 month ago ↶
8
9
use crate :: attest:: Attest;
750251b2 Andy McCall 1 month ago ↶
10
use crate :: cbor:: { self , Value} ;
5b9c6ad4 Andy McCall 1 month ago ↶
11
use crate :: hash:: { ChangeHash, ObjectRef, ObjectType} ;
12
use crate :: identity:: Signature;
750251b2 Andy McCall 1 month ago ↶
13
use crate :: objects:: { self as o, Change, ObjectError} ;
83e0677b Andy McCall 1 month ago ↶
14
use crate :: policy:: Policy;
5b9c6ad4 Andy McCall 1 month ago ↶
15
use crate :: repo:: { Repo, RepoError, Result } ;
2b5ee371 Andy McCall 1 month ago ↶
16
use std:: path:: { Path, PathBuf} ;
9f9e72b9 Andy McCall 1 month ago ↶
17
use std:: sync:: { Arc, Mutex} ;
5b9c6ad4 Andy McCall 1 month ago ↶
18
750251b2 Andy McCall 1 month ago ↶
19
pub const CONTENT_TYPE : & str = " application/oxy-cbor" ;
20
pub const PROTOCOL_VERSION : u64 = 1 ;
21
5b9c6ad4 Andy McCall 1 month ago ↶
22
23
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
24
pub struct SignedAttest {
25
pub attest : Attest,
26
pub sigs : Vec< Signature> ,
27
}
28
83e0677b Andy McCall 1 month ago ↶
29
30
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
31
pub struct SignedPolicy {
32
pub policy : Policy,
33
pub sigs : Vec< Signature> ,
34
}
35
28fc23d7 Andy McCall 1 month ago ↶
36
impl SignedPolicy {
37
38
pub fn encode ( & self ) -> Vec< u8 > {
39
crate :: cbor:: encode( & wire:: policy_value( self ) )
40
}
41
pub fn decode ( b : & [u8 ]) -> Result< SignedPolicy> {
75167672 Andy McCall 1 month ago
42
wire:: policy_from(
43
& crate :: cbor:: decode( b) .map_err ( | e | RepoError:: Object( ObjectError:: from( e) ) ) ? ,
44
)
28fc23d7 Andy McCall 1 month ago ↶
45
}
46
}
47
132b884b Andy McCall 1 month ago ↶
48
83e0677b Andy McCall 1 month ago ↶
49
# [ derive ( Clone, Debug, Default ) ]
50
pub struct RefsResponse {
51
pub refs : Vec< RemoteRef> ,
52
pub policy : Option< SignedPolicy> ,
132b884b Andy McCall 1 month ago ↶
53
pub tags : Vec< TagRef> ,
28fc23d7 Andy McCall 1 month ago ↶
54
55
56
pub history : Vec< SignedPolicy> ,
14edbaaf Andy McCall 1 month ago ↶
57
58
pub default_bookmark : Option< String> ,
4b9d0677 Andy McCall 18 days ago ↶
59
60
61
62
pub log : Option< String> ,
028545df Andy McCall 17 days ago ↶
63
64
65
66
pub witnesses : Vec< ( String, Vec< u8 > ) > ,
83e0677b Andy McCall 1 month ago ↶
67
}
68
4b9d0677 Andy McCall 18 days ago ↶
69
70
71
72
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
73
pub struct Logged {
74
pub index : u64 ,
75
pub entry : Vec< u8 > ,
76
pub proof : Vec< [ u8 ; 32 ] > ,
77
}
78
028545df Andy McCall 17 days ago ↶
79
80
81
82
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
83
pub struct Integration {
84
pub note : String,
85
pub proof : Vec< [ u8 ; 32 ] > ,
86
}
87
132b884b Andy McCall 1 month ago ↶
88
89
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
90
pub struct TagRef {
91
pub name : String,
92
pub state : ObjectRef,
93
pub heads : Vec< ChangeHash> ,
94
pub attestation : Option< SignedAttest> ,
1ec6fdbc Andy McCall 16 days ago ↶
95
96
97
98
pub accepted : Option< SignedAttest> ,
99
100
101
pub logged : Option< Logged> ,
102
103
104
pub integration : Vec< Integration> ,
105
}
106
107
impl TagRef {
108
109
pub fn new (
110
name : String,
111
state : ObjectRef,
112
heads : Vec< ChangeHash> ,
113
attestation : Option< SignedAttest> ,
114
) -> TagRef {
115
TagRef {
116
name,
117
state,
118
heads,
119
attestation,
120
accepted: None ,
121
logged: None ,
122
integration: vec! [ ] ,
123
}
124
}
132b884b Andy McCall 1 month ago ↶
125
}
126
5b9c6ad4 Andy McCall 1 month ago ↶
127
128
# [ derive ( Clone, Debug ) ]
129
pub struct RemoteRef {
130
pub name : String,
131
pub state : ObjectRef,
132
pub heads : Vec< ChangeHash> ,
4161262e Andy McCall 1 month ago ↶
133
134
5b9c6ad4 Andy McCall 1 month ago ↶
135
pub attestation : Option< SignedAttest> ,
4161262e Andy McCall 1 month ago ↶
136
137
138
pub last_serial : Option< u64 > ,
f53cbe7a Andy McCall 19 days ago ↶
139
140
141
pub accepted : Option< SignedAttest> ,
4b9d0677 Andy McCall 18 days ago ↶
142
143
144
pub logged : Option< Logged> ,
028545df Andy McCall 17 days ago ↶
145
146
147
pub integration : Vec< Integration> ,
5b9c6ad4 Andy McCall 1 month ago ↶
148
}
149
150
151
# [ derive ( Clone, Debug, Default ) ]
152
pub struct Bundle {
153
154
pub changes : Vec< ( Change, Vec< Signature> ) > ,
155
156
pub attests : Vec< SignedAttest> ,
157
158
pub objects : Vec< ( ObjectType, Vec< u8 > ) > ,
28fc23d7 Andy McCall 1 month ago ↶
159
160
161
pub policies : Vec< SignedPolicy> ,
598009a7 Andy McCall 1 month ago ↶
162
163
164
165
pub hashes : Vec< ChangeHash> ,
2c75672f Andy McCall 1 month ago ↶
166
167
168
169
pub raw_changes : Vec< ( Vec< u8 > , Vec< Signature> ) > ,
f0a517d2 Andy McCall 19 days ago ↶
170
171
172
pub certificates : Vec< crate:: cert:: SignedCertificate> ,
5b9c6ad4 Andy McCall 1 month ago ↶
173
}
174
175
impl Bundle {
176
pub fn is_empty ( & self ) -> bool {
177
self .changes.is_empty ( ) & & self .attests.is_empty ( ) & & self .objects.is_empty ( )
178
}
179
}
180
181
182
# [ derive ( Clone, Debug ) ]
183
pub struct RefUpdate {
184
pub name : String,
185
pub old_state : ObjectRef,
186
pub new_state : ObjectRef,
187
pub force : bool ,
188
pub attestation : Option< SignedAttest> ,
189
}
190
191
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
192
pub struct RefResult {
193
pub name : String,
194
pub ok : bool ,
195
pub reason : String,
196
}
197
198
pub trait Remote {
199
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > ;
83e0677b Andy McCall 1 month ago ↶
200
fn refs ( & self ) -> Result< RefsResponse> ;
5b9c6ad4 Andy McCall 1 month ago ↶
201
202
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > ;
b3c5a59a Andy McCall 1 month ago ↶
203
204
205
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> ;
206
207
208
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > ;
2863456a Andy McCall 1 month ago ↶
209
210
211
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> ;
80b069fa Andy McCall 10 days ago ↶
212
213
214
215
216
fn checkpoint_index ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
217
self .checkpoint ( heads)
218
}
2863456a Andy McCall 1 month ago ↶
219
220
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > ;
132b884b Andy McCall 1 month ago ↶
221
222
223
75167672 Andy McCall 1 month ago
224
fn push (
225
& self ,
226
bundle : Bundle,
227
updates : Vec< RefUpdate> ,
228
policy : Option< SignedPolicy> ,
229
tags : Vec< TagRef> ,
230
) -> Result< Vec< RefResult> > ;
5b9c6ad4 Andy McCall 1 month ago ↶
231
}
232
750251b2 Andy McCall 1 month ago ↶
233
234
5b9c6ad4 Andy McCall 1 month ago ↶
235
236
pub struct LocalRemote {
237
pub repo : Repo,
84b9e9ac Andy McCall 1 month ago ↶
238
239
240
pub claimed_id : Option< [ u8 ; 16 ] > ,
5b9c6ad4 Andy McCall 1 month ago ↶
241
}
242
243
impl LocalRemote {
244
pub fn open ( path : & Path) -> Result< LocalRemote> {
75167672 Andy McCall 1 month ago
245
Ok ( LocalRemote {
246
repo: Repo:: open( path) ? ,
84b9e9ac Andy McCall 1 month ago ↶
247
claimed_id: None ,
75167672 Andy McCall 1 month ago
248
} )
5b9c6ad4 Andy McCall 1 month ago ↶
249
}
250
}
251
252
impl Remote for LocalRemote {
253
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > {
254
self .repo.id ( )
255
}
83e0677b Andy McCall 1 month ago ↶
256
fn refs ( & self ) -> Result< RefsResponse> {
5b9c6ad4 Andy McCall 1 month ago ↶
257
self .repo.remote_refs ( )
258
}
259
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
260
self .repo.negotiate ( have, want)
261
}
b3c5a59a Andy McCall 1 month ago ↶
262
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
2c75672f Andy McCall 1 month ago ↶
263
self .repo.bundle_raw_for ( changes, max_blob)
b3c5a59a Andy McCall 1 month ago ↶
264
}
265
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
266
self .repo.objects_for ( refs)
5b9c6ad4 Andy McCall 1 month ago ↶
267
}
2863456a Andy McCall 1 month ago ↶
268
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
269
self .repo.checkpoint_bundle_for ( heads)
80b069fa Andy McCall 10 days ago ↶
270
}
271
fn checkpoint_index ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
272
self .repo.checkpoint_index_for ( heads)
2863456a Andy McCall 1 month ago ↶
273
}
274
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
275
self .repo.shallow_changes ( heads, depth)
276
}
75167672 Andy McCall 1 month ago
277
fn push (
278
& self ,
279
bundle : Bundle,
280
updates : Vec< RefUpdate> ,
281
policy : Option< SignedPolicy> ,
282
tags : Vec< TagRef> ,
283
) -> Result< Vec< RefResult> > {
84b9e9ac Andy McCall 1 month ago ↶
284
if self .repo.is_unbound ( ) {
285
match self .claimed_id {
286
Some ( id) => self .repo.bind_id ( id) ? ,
eae59556 Andy McCall 1 month ago ↶
287
None => {
288
return Err ( RepoError:: Msg(
289
" the remote is unbound and this client did not say which repository it is"
290
.into ( ) ,
291
) )
292
}
84b9e9ac Andy McCall 1 month ago ↶
293
}
294
}
132b884b Andy McCall 1 month ago ↶
295
self .repo.receive_push ( bundle, updates, policy, tags)
5b9c6ad4 Andy McCall 1 month ago ↶
296
}
297
}
298
750251b2 Andy McCall 1 month ago ↶
299
300
2b5ee371 Andy McCall 1 month ago ↶
301
302
# [ derive ( Clone, Debug, Default ) ]
303
pub struct RemoteOptions {
304
305
pub token : Option< String> ,
306
307
308
pub ca : Option< PathBuf> ,
75167672 Andy McCall 1 month ago
309
310
311
pub repo_id : Option< [ u8 ; 16 ] > ,
9f9e72b9 Andy McCall 1 month ago ↶
312
313
314
315
316
pub cache_dir : Option< PathBuf> ,
68a92484 Andy McCall 22 days ago ↶
317
569802eb Andy McCall 7 days ago ↶
318
319
320
68a92484 Andy McCall 22 days ago ↶
321
pub fallback_tokens : Vec< String> ,
0588de69 Andy McCall 7 days ago ↶
322
323
324
325
326
pub fallback_on_401_only : bool ,
2b5ee371 Andy McCall 1 month ago ↶
327
}
328
9f9e72b9 Andy McCall 1 month ago ↶
329
330
331
332
pub type StoreOpener =
333
fn ( & str , & RemoteOptions) -> Option< crate:: strata:: Result< Box< dyn crate:: strata:: Store> > > ;
334
335
static STORE_OPENERS : Mutex< Vec< StoreOpener> > = Mutex:: new( Vec :: new( ) ) ;
336
337
pub fn register_store_opener ( opener : StoreOpener) {
338
STORE_OPENERS
339
.lock ( )
340
.unwrap_or_else ( | p | p.into_inner ( ) )
341
.push ( opener) ;
342
}
343
344
345
pub fn open_store (
346
url : & str ,
347
options : & RemoteOptions,
348
) -> Option< crate:: strata:: Result< Box< dyn crate:: strata:: Store> > > {
349
if let Some ( path) = url.strip_prefix ( " dir://" ) {
350
return Some (
351
crate :: strata:: LocalStore:: open( path)
352
.map ( | s | Box :: new( s) as Box< dyn crate:: strata:: Store> ) ,
353
) ;
354
}
355
let openers = STORE_OPENERS .lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) ;
356
openers.iter ( ) .find_map ( | open | open ( url, options) )
357
}
358
75167672 Andy McCall 1 month ago
359
360
pub const REPO_ID_HEADER : & str = " Oxy-Repo-Id" ;
361
362
363
pub const UNBOUND_ID : [ u8 ; 16 ] = [ 0 ; 16 ] ;
364
ee130dd9 Andy McCall 16 days ago ↶
365
366
367
368
369
pub const FEATURES_HEADER : & str = " Oxy-Features" ;
370
371
372
373
374
375
pub const FEATURE_CERTIFIED_TAGS : & str = " certified-tags" ;
376
377
378
pub const CLIENT_FEATURES : & [ & str ] = & [ FEATURE_CERTIFIED_TAGS ] ;
379
380
381
pub fn user_agent ( ) -> String {
382
format! ( " oxy/{} " , env! ( " CARGO_PKG_VERSION" ) )
383
}
384
385
386
387
388
pub fn declares_feature ( header : Option< & str > , feature : & str ) -> bool {
389
header.is_some_and ( | h | h.split ( ' ,' ) .any ( | f | f.trim ( ) .eq_ignore_ascii_case ( feature) ) )
390
}
391
750251b2 Andy McCall 1 month ago ↶
392
393
pub struct HttpRemote {
394
base : String,
68a92484 Andy McCall 22 days ago ↶
395
396
token : Mutex< Option< String> > ,
397
fallbacks : Mutex< Vec< String> > ,
0588de69 Andy McCall 7 days ago ↶
398
fallback_on_401_only : bool ,
75167672 Andy McCall 1 month ago
399
repo_id : Option< [ u8 ; 16 ] > ,
750251b2 Andy McCall 1 month ago ↶
400
agent : ureq:: Agent,
401
}
402
2b5ee371 Andy McCall 1 month ago ↶
403
404
fn tls_client_config ( ca : Option< & Path> ) -> Result< Arc< rustls:: ClientConfig> > {
405
let mut roots = rustls:: RootCertStore:: empty( ) ;
406
roots.extend ( webpki_roots:: TLS_SERVER_ROOTS .iter ( ) .cloned ( ) ) ;
407
if let Some ( path) = ca {
75167672 Andy McCall 1 month ago
408
let pem = std:: fs:: read( path)
409
.map_err ( | e | RepoError:: Msg( format! ( " reading CA file {} : {e} " , path.display ( ) ) ) ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
410
let certs = rustls_pemfile:: certs( & mut pem.as_slice ( ) )
411
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( )
412
.map_err ( | e | RepoError:: Msg( format! ( " parsing CA file {} : {e} " , path.display ( ) ) ) ) ? ;
413
if certs.is_empty ( ) {
75167672 Andy McCall 1 month ago
414
return Err ( RepoError:: Msg( format! (
415
" CA file {} holds no certificates" ,
416
path.display ( )
417
) ) ) ;
2b5ee371 Andy McCall 1 month ago ↶
418
}
419
for c in certs {
75167672 Andy McCall 1 month ago
420
roots
421
.add ( c)
422
.map_err ( | e | RepoError:: Msg( format! ( " CA file {} : {e} " , path.display ( ) ) ) ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
423
}
424
}
75167672 Andy McCall 1 month ago
425
let cfg = rustls:: ClientConfig:: builder_with_provider( Arc:: new(
426
rustls:: crypto:: ring:: default_provider( ) ,
427
) )
428
.with_safe_default_protocol_versions ( )
429
.map_err ( | e | RepoError:: Msg( e.to_string ( ) ) ) ?
430
.with_root_certificates ( roots)
431
.with_no_client_auth ( ) ;
2b5ee371 Andy McCall 1 month ago ↶
432
Ok ( Arc:: new( cfg) )
433
}
434
750251b2 Andy McCall 1 month ago ↶
435
impl HttpRemote {
2b5ee371 Andy McCall 1 month ago ↶
436
pub fn new ( base : & str , options : & RemoteOptions) -> Result< HttpRemote> {
ff1f62fe Andy McCall 1 month ago ↶
437
refuse_plain_token ( base, options) ? ;
75167672 Andy McCall 1 month ago
438
let agent = ureq:: AgentBuilder:: new( )
439
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
ee130dd9 Andy McCall 16 days ago ↶
440
.user_agent ( & user_agent ( ) )
75167672 Andy McCall 1 month ago
441
.build ( ) ;
442
Ok ( HttpRemote {
443
base: base.trim_end_matches ( ' /' ) .to_string ( ) ,
68a92484 Andy McCall 22 days ago ↶
444
token: Mutex:: new( options.token.clone ( ) ) ,
445
fallbacks: Mutex:: new( options.fallback_tokens.iter ( ) .rev ( ) .cloned ( ) .collect ( ) ) ,
0588de69 Andy McCall 7 days ago ↶
446
fallback_on_401_only: options.fallback_on_401_only,
75167672 Andy McCall 1 month ago
447
repo_id: options.repo_id,
448
agent,
449
} )
750251b2 Andy McCall 1 month ago ↶
450
}
451
68a92484 Andy McCall 22 days ago ↶
452
453
750251b2 Andy McCall 1 month ago ↶
454
fn call ( & self , post : bool , endpoint : & str , body : Option< & [ u8 ] > ) -> Result< Vec< u8 > > {
68a92484 Andy McCall 22 days ago ↶
455
loop {
456
let token = self .token.lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) .clone ( ) ;
457
match self .call_once ( post, endpoint, body, token.as_deref ( ) ) {
0588de69 Andy McCall 7 days ago ↶
458
Err ( RepoError:: Msg( m) )
459
if token.is_some ( )
460
& & refused ( & m)
461
& & ( ! self .fallback_on_401_only | | m.contains ( " : HTTP 401" ) ) =>
462
{
68a92484 Andy McCall 22 days ago ↶
463
let next = self
464
.fallbacks
465
.lock ( )
466
.unwrap_or_else ( | p | p.into_inner ( ) )
467
.pop ( ) ;
468
let Some ( next) = next else {
469
return Err ( RepoError:: Msg( m) ) ;
470
} ;
471
crate :: credentials:: warn_once( & format! (
569802eb Andy McCall 7 days ago ↶
472
" {} : the token in use was refused; trying another this computer has for the hub \
473
(a stale remote.NAME.token goes with `oxy config --unset remote.NAME.token`)" ,
68a92484 Andy McCall 22 days ago ↶
474
self .base
475
) ) ;
476
* self .token.lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) = Some ( next) ;
477
}
478
other => return other,
479
}
480
}
481
}
482
483
fn call_once (
484
& self ,
485
post : bool ,
486
endpoint : & str ,
487
body : Option< & [ u8 ] > ,
488
token : Option< & str > ,
489
) -> Result< Vec< u8 > > {
750251b2 Andy McCall 1 month ago ↶
490
let url = format! ( " {} /oxy/v1/{endpoint} " , self .base) ;
75167672 Andy McCall 1 month ago
491
let mut req = if post {
492
self .agent.post ( & url)
493
} else {
494
self .agent.get ( & url)
495
} ;
ee130dd9 Andy McCall 16 days ago ↶
496
req = req
497
.set ( " Accept" , CONTENT_TYPE )
498
.set ( FEATURES_HEADER , & CLIENT_FEATURES .join ( " ," ) ) ;
68a92484 Andy McCall 22 days ago ↶
499
if let Some ( t) = token {
750251b2 Andy McCall 1 month ago ↶
500
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
501
}
75167672 Andy McCall 1 month ago
502
if let Some ( id) = & self .repo_id {
503
req = req.set ( REPO_ID_HEADER , & hex:: encode( id) ) ;
504
}
750251b2 Andy McCall 1 month ago ↶
505
let resp = match body {
506
Some ( b) => req.set ( " Content-Type" , CONTENT_TYPE ) .send_bytes ( b) ,
507
None => req.call ( ) ,
508
} ;
509
match resp {
510
Ok ( r) => {
511
let mut out = Vec :: new( ) ;
75167672 Andy McCall 1 month ago
512
r.into_reader ( )
513
.read_to_end ( & mut out)
514
.map_err ( | e | RepoError:: Msg( format! ( " {url} : {e} " ) ) ) ? ;
750251b2 Andy McCall 1 month ago ↶
515
Ok ( out)
516
}
517
Err ( ureq:: Error:: Status( code, r) ) => {
518
let text = r.into_string ( ) .unwrap_or_default ( ) ;
67a4fe1a Andy McCall 16 days ago ↶
519
520
d3b27090 Andy McCall 11 days ago ↶
521
522
523
67a4fe1a Andy McCall 16 days ago ↶
524
let status = format! ( " {url} : HTTP {code} " ) ;
d3b27090 Andy McCall 11 days ago ↶
525
let msg = format! ( " {status} : {} " , plain_error_text ( & text) ) ;
67a4fe1a Andy McCall 16 days ago ↶
526
Err ( RepoError:: Msg( match hint_for ( & status) {
527
Some ( h) => format! ( " {msg} \n hint: {h} " ) ,
528
None => msg,
529
} ) )
750251b2 Andy McCall 1 month ago ↶
530
}
3e5772cc Andy McCall 1 month ago ↶
531
532
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
750251b2 Andy McCall 1 month ago ↶
533
}
534
}
68a92484 Andy McCall 22 days ago ↶
535
}
536
537
538
539
540
fn refused ( message : & str ) -> bool {
541
[ " : HTTP 401" , " : HTTP 403" , " : HTTP 404" ]
542
.iter ( )
543
.any ( | c | message.contains ( c) )
750251b2 Andy McCall 1 month ago ↶
544
}
545
ff1f62fe Andy McCall 1 month ago ↶
546
547
548
fn refuse_plain_token ( url : & str , options : & RemoteOptions) -> Result< ( ) > {
549
if options.token.is_some ( ) & & ! crate :: credentials:: token_allowed( url) {
550
return Err ( RepoError:: Msg( format! (
551
" {url} : refusing to send a token over plain http; use https"
552
) ) ) ;
553
}
554
Ok ( ( ) )
555
}
556
3e5772cc Andy McCall 1 month ago ↶
557
558
pub fn hint_for ( message : & str ) -> Option< & 'static str > {
559
let m = message.to_ascii_lowercase ( ) ;
560
let hint = if m.contains ( " http 401" ) {
ff1f62fe Andy McCall 1 month ago ↶
561
" the hub needs you signed in, or did not accept the token: run `oxy auth login HUB` (pushing needs repo:write)"
3e5772cc Andy McCall 1 month ago ↶
562
} else if m.contains ( " http 403" ) {
563
" the token or account lacks permission: pushing needs a token with repo:write and write access to the repository"
d3b27090 Andy McCall 11 days ago ↶
564
} else if m.contains ( " http 413" ) {
565
" the request was larger than the server, or a proxy in front of it, takes at once (Cloudflare's limit is 100 MB): push in smaller parts with `oxy config push.batch-bytes 33554432`, then push again"
3e5772cc Andy McCall 1 month ago ↶
566
} else if m.contains ( " http 404" ) {
ff1f62fe Andy McCall 1 month ago ↶
567
" nothing at this URL that you can see: check the owner and repository name; a private repository needs you signed in (`oxy auth login HUB`)"
3e5772cc Andy McCall 1 month ago ↶
568
} else if m.contains ( " unknownissuer" )
569
| | m.contains ( " unknown issuer" )
570
| | m.contains ( " certificate" )
571
| | m.contains ( " invalidcertificate" )
572
{
573
" the server's certificate is not trusted: for a hub with a self-signed certificate, download it from /.well-known/oxyhub-ca.pem on the hub and run `oxy config remote.<remote>.ca FILE`"
574
} else if m.contains ( " connection refused" ) {
575
" nothing is listening there: check the URL and port, and that the server is running"
576
} else if m.contains ( " dns" )
577
| | m.contains ( " failed to lookup" )
578
| | m.contains ( " name or service not known" )
579
{
580
" the host name does not resolve: check the URL"
581
} else if m.contains ( " timed out" ) | | m.contains ( " timeout" ) {
582
" the server did not answer in time: check the network, or try again"
583
} else {
584
return None ;
585
} ;
586
Some ( hint)
587
}
588
d3b27090 Andy McCall 11 days ago ↶
589
590
591
fn plain_error_text ( text : & str ) -> String {
592
let t = text.trim ( ) ;
593
let lower = t.to_ascii_lowercase ( ) ;
594
if ! ( lower.starts_with ( " <!doctype html" ) | | lower.starts_with ( " <html" ) ) {
595
return t.to_string ( ) ;
596
}
597
let title = lower
598
.find ( " <title>" )
599
.and_then ( | i | {
600
lower[ i + 7 .. ]
601
.find ( " </title>" )
602
.map ( | j | t[ i + 7 .. i + 7 + j] .trim ( ) )
603
} )
604
.filter ( | s | ! s.is_empty ( ) ) ;
605
match title {
606
Some ( s) => format! ( " {s} (an HTML error page, from a proxy in front of the server perhaps)" ) ,
607
None => " an HTML error page (from a proxy in front of the server perhaps)" .into ( ) ,
608
}
609
}
610
3e5772cc Andy McCall 1 month ago ↶
611
612
pub fn with_hint ( message : String) -> String {
613
match hint_for ( & message) {
614
Some ( h) => format! ( " {message} \n hint: {h} " ) ,
615
None => message,
ff1f62fe Andy McCall 1 month ago ↶
616
}
617
}
618
619
620
621
622
pub fn http_json (
623
method : & str ,
624
url : & str ,
625
body : Option< & str > ,
626
options : & RemoteOptions,
627
) -> Result< ( u16 , String) > {
628
refuse_plain_token ( url, options) ? ;
629
let agent = ureq:: AgentBuilder:: new( )
630
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
631
.timeout ( std:: time:: Duration:: from_secs( 60 ) )
ee130dd9 Andy McCall 16 days ago ↶
632
.user_agent ( & user_agent ( ) )
ff1f62fe Andy McCall 1 month ago ↶
633
.build ( ) ;
634
let mut req = agent.request ( method, url) .set ( " Accept" , " application/json" ) ;
635
if let Some ( t) = & options.token {
636
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
637
}
638
let resp = match body {
639
Some ( b) => req.set ( " Content-Type" , " application/json" ) .send_string ( b) ,
640
None => req.call ( ) ,
641
} ;
642
match resp {
643
Ok ( r) => {
644
let code = r.status ( ) ;
645
Ok ( ( code, r.into_string ( ) .unwrap_or_default ( ) ) )
646
}
647
Err ( ureq:: Error:: Status( code, r) ) => Ok ( ( code, r.into_string ( ) .unwrap_or_default ( ) ) ) ,
648
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
3e5772cc Andy McCall 1 month ago ↶
649
}
650
}
651
d82e4409 Andy McCall 1 month ago ↶
652
653
654
pub fn http_get ( url : & str , options : & RemoteOptions) -> Result< Vec< u8 > > {
ff1f62fe Andy McCall 1 month ago ↶
655
refuse_plain_token ( url, options) ? ;
d82e4409 Andy McCall 1 month ago ↶
656
let agent = ureq:: AgentBuilder:: new( )
657
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
ee130dd9 Andy McCall 16 days ago ↶
658
.user_agent ( & user_agent ( ) )
d82e4409 Andy McCall 1 month ago ↶
659
.build ( ) ;
660
let mut req = agent.get ( url) .set ( " Accept" , " application/json" ) ;
661
if let Some ( t) = & options.token {
662
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
663
}
664
match req.call ( ) {
665
Ok ( r) => {
666
let mut out = Vec :: new( ) ;
667
r.into_reader ( )
668
.read_to_end ( & mut out)
669
.map_err ( | e | RepoError:: Msg( format! ( " {url} : {e} " ) ) ) ? ;
670
Ok ( out)
671
}
672
Err ( ureq:: Error:: Status( code, r) ) => {
673
let text = r.into_string ( ) .unwrap_or_default ( ) ;
3e5772cc Andy McCall 1 month ago ↶
674
Err ( RepoError:: Msg( with_hint ( format! (
4213c41d Andy McCall 1 month ago ↶
675
" {url} : HTTP {code} : {} " ,
676
text.trim ( )
3e5772cc Andy McCall 1 month ago ↶
677
) ) ) )
d82e4409 Andy McCall 1 month ago ↶
678
}
3e5772cc Andy McCall 1 month ago ↶
679
680
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
d82e4409 Andy McCall 1 month ago ↶
681
}
682
}
683
750251b2 Andy McCall 1 month ago ↶
684
impl Remote for HttpRemote {
685
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > {
686
wire:: decode_capabilities( & self .call ( false , " capabilities" , None ) ? )
687
}
83e0677b Andy McCall 1 month ago ↶
688
fn refs ( & self ) -> Result< RefsResponse> {
750251b2 Andy McCall 1 month ago ↶
689
Ok ( wire:: decode_refs( & self .call ( false , " refs" , None ) ? ) ? .1 )
690
}
691
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
75167672 Andy McCall 1 month ago
692
wire:: decode_hashes( & self .call (
693
true ,
694
" negotiate" ,
695
Some ( & wire:: encode_negotiate( have, want) ) ,
696
) ? )
750251b2 Andy McCall 1 month ago ↶
697
}
b3c5a59a Andy McCall 1 month ago ↶
698
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
75167672 Andy McCall 1 month ago
699
wire:: decode_bundle( & self .call (
700
true ,
701
" fetch" ,
702
Some ( & wire:: encode_fetch( changes, max_blob) ) ,
703
) ? )
750251b2 Andy McCall 1 month ago ↶
704
}
b3c5a59a Andy McCall 1 month ago ↶
705
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
75167672 Andy McCall 1 month ago
706
Ok (
707
wire:: decode_bundle( & self .call ( true , " objects" , Some ( & wire:: encode_hashes( refs) ) ) ? ) ?
708
.objects,
709
)
b3c5a59a Andy McCall 1 month ago ↶
710
}
2863456a Andy McCall 1 month ago ↶
711
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
f3811eb2 Andy McCall 11 days ago ↶
712
let body = {
713
let _t = crate :: timing:: phase( " remote: checkpoint answer" ) ;
714
self .call ( true , " checkpoint" , Some ( & wire:: encode_hashes( heads) ) ) ?
715
} ;
716
let _t = crate :: timing:: phase( " remote: decode the checkpoint" ) ;
80b069fa Andy McCall 10 days ago ↶
717
wire:: decode_bundle( & body)
718
}
719
fn checkpoint_index ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
720
let body = {
721
let _t = crate :: timing:: phase( " remote: checkpoint index" ) ;
722
match self .call ( true , " checkpoint-index" , Some ( & wire:: encode_hashes( heads) ) ) {
723
Ok ( b) => b,
724
725
Err ( RepoError:: Msg( m) ) if m.contains ( " HTTP 404" ) => return self .checkpoint ( heads) ,
726
Err ( e) => return Err ( e) ,
727
}
728
} ;
f3811eb2 Andy McCall 11 days ago ↶
729
wire:: decode_bundle( & body)
2863456a Andy McCall 1 month ago ↶
730
}
731
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
75167672 Andy McCall 1 month ago
732
wire:: decode_hashes( & self .call (
733
true ,
734
" shallow" ,
735
Some ( & wire:: encode_shallow( heads, depth) ) ,
736
) ? )
2863456a Andy McCall 1 month ago ↶
737
}
75167672 Andy McCall 1 month ago
738
fn push (
739
& self ,
740
bundle : Bundle,
741
updates : Vec< RefUpdate> ,
742
policy : Option< SignedPolicy> ,
743
tags : Vec< TagRef> ,
744
) -> Result< Vec< RefResult> > {
745
wire:: decode_push_response( & self .call (
746
true ,
747
" push" ,
748
Some ( & wire:: encode_push(
749
& bundle,
750
& updates,
751
policy.as_ref ( ) ,
752
& tags,
753
) ) ,
754
) ? )
750251b2 Andy McCall 1 month ago ↶
755
}
756
}
757
827281ee Andy McCall 1 month ago ↶
758
759
760
761
pub fn stored_url ( url : & str ) -> String {
8d803563 Andy McCall 1 month ago ↶
762
763
9f9e72b9 Andy McCall 1 month ago ↶
764
let ( prefix, path) = match ( url.strip_prefix ( " file://" ) , url.strip_prefix ( " dir://" ) ) {
765
( Some ( p) , _ ) => ( " file://" , p) ,
766
( _ , Some ( p) ) => ( " dir://" , p) ,
8d803563 Andy McCall 1 month ago ↶
767
_ if url.contains ( " ://" ) => return url.to_string ( ) ,
9f9e72b9 Andy McCall 1 month ago ↶
768
_ => ( " " , url) ,
827281ee Andy McCall 1 month ago ↶
769
} ;
770
match std:: path:: absolute( path) {
771
Ok ( abs) => format! ( " {prefix} {} " , abs.display ( ) ) ,
772
Err ( _ ) => url.to_string ( ) ,
773
}
774
}
775
9f9e72b9 Andy McCall 1 month ago ↶
776
777
2b5ee371 Andy McCall 1 month ago ↶
778
pub fn open_remote ( url : & str , options : & RemoteOptions) -> Result< Box< dyn Remote> > {
750251b2 Andy McCall 1 month ago ↶
779
if url.starts_with ( " http://" ) | | url.starts_with ( " https://" ) {
2b5ee371 Andy McCall 1 month ago ↶
780
return Ok ( Box :: new( HttpRemote:: new( url, options) ? ) ) ;
9f9e72b9 Andy McCall 1 month ago ↶
781
}
782
if let Some ( store) = open_store ( url, options) {
783
return crate :: repo:: strata:: open_store_remote( store? , options) ;
750251b2 Andy McCall 1 month ago ↶
784
}
5b9c6ad4 Andy McCall 1 month ago ↶
785
let path = url.strip_prefix ( " file://" ) .unwrap_or ( url) ;
786
if path.contains ( " ://" ) {
750251b2 Andy McCall 1 month ago ↶
787
return Err ( RepoError:: Msg( format! ( " unsupported remote URL {url:?} " ) ) ) ;
5b9c6ad4 Andy McCall 1 month ago ↶
788
}
84b9e9ac Andy McCall 1 month ago ↶
789
let mut local = LocalRemote:: open( Path:: new( path) ) ? ;
790
local.claimed_id = options.repo_id;
791
Ok ( Box :: new( local) )
5b9c6ad4 Andy McCall 1 month ago ↶
792
}
750251b2 Andy McCall 1 month ago ↶
793
794
795
796
797
pub mod wire {
798
use super :: * ;
799
800
fn schema ( msg : & str ) -> RepoError {
801
RepoError:: Object( ObjectError:: Schema( msg.into ( ) ) )
802
}
803
fn sigs_value ( sigs : & [Signature]) -> Value {
804
Value:: Array( sigs.iter ( ) .map ( | s | s.to_value ( ) ) .collect ( ) )
805
}
806
fn sigs_from ( v : & Value) -> Result< Vec< Signature> > {
75167672 Andy McCall 1 month ago
807
Ok ( o:: array( v, " signatures" ) ?
808
.iter ( )
809
.map ( Signature:: from_value)
810
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( ) ? )
750251b2 Andy McCall 1 month ago ↶
811
}
812
fn hashes_value ( hs : & [ObjectRef]) -> Value {
813
Value:: Array( hs.iter ( ) .map ( | h | Value:: Bytes( h.0. to_vec ( ) ) ) .collect ( ) )
814
}
815
fn hashes_from ( v : & Value) -> Result< Vec< ObjectRef> > {
75167672 Andy McCall 1 month ago
816
Ok ( o:: array( v, " hashes" ) ?
817
.iter ( )
818
.map ( | h | o:: oref( h, " hash" ) )
819
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( ) ? )
750251b2 Andy McCall 1 month ago ↶
820
}
821
fn attest_value ( sa : & SignedAttest) -> Value {
822
Value:: Array( vec! [ Value:: Bytes( sa.attest.encode ( ) ) , sigs_value ( & sa.sigs) ] )
823
}
824
fn attest_from ( v : & Value) -> Result< SignedAttest> {
825
let a = o:: array( v, " signed attest" ) ? ;
826
if a.len ( ) ! = 2 {
827
return Err ( schema ( " signed attest: expected [attest, sigs]" ) ) ;
828
}
75167672 Andy McCall 1 month ago
829
Ok ( SignedAttest {
830
attest: Attest:: decode( o:: bytes( & a[ 0 ] , " attest" ) ? ) ? ,
831
sigs: sigs_from ( & a[ 1 ] ) ? ,
832
} )
750251b2 Andy McCall 1 month ago ↶
833
}
834
fn opt_attest_value ( sa : & Option< SignedAttest> ) -> Value {
835
sa.as_ref ( ) .map ( attest_value) .unwrap_or ( Value:: Null)
836
}
837
fn opt_attest_from ( v : & Value) -> Result< Option< SignedAttest> > {
75167672 Andy McCall 1 month ago
838
if v.is_null ( ) {
839
Ok ( None )
840
} else {
841
attest_from ( v) .map ( Some )
842
}
750251b2 Andy McCall 1 month ago ↶
843
}
28fc23d7 Andy McCall 1 month ago ↶
844
845
846
pub fn policy_value ( sp : & SignedPolicy) -> Value {
847
Value:: Array( vec! [ Value:: Bytes( sp.policy.encode ( ) ) , sigs_value ( & sp.sigs) ] )
83e0677b Andy McCall 1 month ago ↶
848
}
28fc23d7 Andy McCall 1 month ago ↶
849
pub fn policy_from ( v : & Value) -> Result< SignedPolicy> {
83e0677b Andy McCall 1 month ago ↶
850
let a = o:: array( v, " signed policy" ) ? ;
851
if a.len ( ) ! = 2 {
852
return Err ( schema ( " signed policy: expected [policy, sigs]" ) ) ;
853
}
75167672 Andy McCall 1 month ago
854
Ok ( SignedPolicy {
855
policy: Policy:: decode( o:: bytes( & a[ 0 ] , " policy" ) ? ) ? ,
856
sigs: sigs_from ( & a[ 1 ] ) ? ,
857
} )
28fc23d7 Andy McCall 1 month ago ↶
858
}
859
fn opt_policy_value ( sp : Option< & SignedPolicy> ) -> Value {
860
sp.map ( policy_value) .unwrap_or ( Value:: Null)
861
}
862
fn opt_policy_from ( v : & Value) -> Result< Option< SignedPolicy> > {
75167672 Andy McCall 1 month ago
863
if v.is_null ( ) {
864
Ok ( None )
865
} else {
866
policy_from ( v) .map ( Some )
867
}
83e0677b Andy McCall 1 month ago ↶
868
}
750251b2 Andy McCall 1 month ago ↶
869
fn map < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a o:: Map> {
870
Ok ( o:: map( v, what) ? )
871
}
872
fn field < 'a > ( m : & 'a o:: Map, k : u64 , what : & str ) -> Result< & 'a Value> {
873
Ok ( o:: field( m, k, what) ? )
874
}
875
6f8aeb99 Andy McCall 1 month ago ↶
876
877
878
750251b2 Andy McCall 1 month ago ↶
879
pub fn bundle_value ( b : & Bundle) -> Value {
2c75672f Andy McCall 1 month ago ↶
880
let mut changes = Vec :: with_capacity( b.raw_changes.len ( ) + b.changes.len ( ) ) ;
881
882
883
for ( bytes, s) in & b.raw_changes {
884
changes.push ( Value:: Array( vec! [
885
Value:: Bytes( bytes.clone ( ) ) ,
886
sigs_value ( s) ,
887
] ) ) ;
888
}
6f8aeb99 Andy McCall 1 month ago ↶
889
let mut texts: Vec< ( ObjectType, Vec< u8 > ) > = Vec :: new( ) ;
890
let mut listed: Option< std:: collections:: HashSet< ObjectRef> > = None ;
891
for ( c, s) in & b.changes {
892
let ( bytes, stored) = c.encode_parts ( ) ;
893
for t in stored {
894
let listed = listed.get_or_insert_with ( | | {
895
b.objects
896
.iter ( )
897
.map ( | ( ty , body ) | crate :: hash:: hash_object( * ty, body) )
898
.collect ( )
899
} ) ;
900
if ! listed.insert ( t.blob.hash ( ) ) {
901
continue ;
902
}
903
for chunk in crate :: chunk:: chunks( & t.content) {
904
if listed.insert ( crate :: hash:: hash_object( ObjectType:: Chunk, chunk) ) {
905
texts.push ( ( ObjectType:: Chunk, chunk.to_vec ( ) ) ) ;
906
}
907
}
908
texts.push ( ( ObjectType:: Blob, t.blob.encode ( ) ) ) ;
909
}
910
changes.push ( Value:: Array( vec! [ Value:: Bytes( bytes) , sigs_value ( s) ] ) ) ;
911
}
f0a517d2 Andy McCall 19 days ago ↶
912
let mut v = Value:: map( [
6f8aeb99 Andy McCall 1 month ago ↶
913
( 0 , Value:: Array( changes) ) ,
750251b2 Andy McCall 1 month ago ↶
914
(
75167672 Andy McCall 1 month ago
915
1 ,
916
Value:: Array( b.attests.iter ( ) .map ( attest_value) .collect ( ) ) ,
917
) ,
918
(
750251b2 Andy McCall 1 month ago ↶
919
2 ,
920
Value:: Array(
75167672 Andy McCall 1 month ago
921
b.objects
922
.iter ( )
6f8aeb99 Andy McCall 1 month ago ↶
923
.chain ( & texts)
75167672 Andy McCall 1 month ago
924
.map ( | ( t , body ) | {
925
Value:: Array( vec! [
926
Value:: Text( t.name ( ) .into ( ) ) ,
927
Value:: Bytes( body.clone ( ) ) ,
928
] )
929
} )
930
.collect ( ) ,
750251b2 Andy McCall 1 month ago ↶
931
) ,
932
) ,
75167672 Andy McCall 1 month ago
933
(
934
3 ,
935
Value:: Array( b.policies.iter ( ) .map ( policy_value) .collect ( ) ) ,
936
) ,
f0a517d2 Andy McCall 19 days ago ↶
937
] ) ;
938
939
if ! b.certificates.is_empty ( ) {
940
if let Value:: Map( m) = & mut v {
941
m.insert (
942
4 ,
943
Value:: Array(
944
b.certificates
945
.iter ( )
946
.map ( | c | Value:: Bytes( c.encode ( ) ) )
947
.collect ( ) ,
948
) ,
949
) ;
950
}
951
}
952
v
750251b2 Andy McCall 1 month ago ↶
953
}
954
955
pub fn bundle_from_value ( v : & Value) -> Result< Bundle> {
956
let m = map ( v, " bundle" ) ? ;
957
let mut b = Bundle:: default( ) ;
958
for ob in o:: array( field ( m, 2 , " objects" ) ? , " objects" ) ? {
959
let pair = o:: array( ob, " object" ) ? ;
960
if pair.len ( ) ! = 2 {
961
return Err ( schema ( " object: expected [type, body]" ) ) ;
962
}
75167672 Andy McCall 1 month ago
963
let ty = ObjectType:: from_name( o:: text( & pair[ 0 ] , " object type" ) ? )
964
.ok_or_else ( | | schema ( " unknown object type" ) ) ? ;
965
b.objects
966
.push ( ( ty, o:: bytes( & pair[ 1 ] , " object body" ) ? .to_vec ( ) ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
967
}
968
969
let mut by_ref: Option< std:: collections:: HashMap< ObjectRef, usize > > = None ;
970
for c in o:: array( field ( m, 0 , " changes" ) ? , " changes" ) ? {
971
let pair = o:: array( c, " change entry" ) ? ;
972
if pair.len ( ) ! = 2 {
973
return Err ( schema ( " change entry: expected [change, sigs]" ) ) ;
974
}
598009a7 Andy McCall 1 month ago ↶
975
let encoded = o:: bytes( & pair[ 0 ] , " change" ) ? ;
976
b.hashes
977
.push ( crate :: hash:: hash_object( ObjectType:: Change, encoded) ) ;
0f93f49c Andy McCall 1 month ago ↶
978
let change = Change:: decode_with( encoded, & mut | text, count, form| {
6f8aeb99 Andy McCall 1 month ago ↶
979
let by_ref = by_ref.get_or_insert_with ( | | {
980
b.objects
981
.iter ( )
982
.enumerate ( )
983
.map ( | ( i , (ty , body ) ) | ( crate :: hash:: hash_object( * ty, body) , i) )
984
.collect ( )
985
} ) ;
986
let get =
987
| r : & ObjectRef, want : ObjectType| match by_ref.get ( r) .map ( | i | & b.objects[ * i] ) {
988
Some ( ( ty, body) ) if * ty = = want => Ok ( body) ,
989
_ => Err ( ObjectError:: Schema( format! (
990
" the bundle lacks {} {} " ,
991
want.name ( ) ,
992
r.short ( 12 )
993
) ) ) ,
994
} ;
995
let blob = crate :: objects:: Blob:: decode( get ( text, ObjectType:: Blob) ? ) ? ;
996
let mut content = Vec :: with_capacity( blob.length as usize ) ;
997
for chunk in & blob.chunks {
998
content.extend_from_slice ( get ( chunk, ObjectType:: Chunk) ? ) ;
999
}
0f93f49c Andy McCall 1 month ago ↶
1000
crate :: objects:: text_lines( & content, count, form)
6f8aeb99 Andy McCall 1 month ago ↶
1001
} ) ? ;
1002
b.changes.push ( ( change, sigs_from ( & pair[ 1 ] ) ? ) ) ;
1003
}
1004
for a in o:: array( field ( m, 1 , " attests" ) ? , " attests" ) ? {
1005
b.attests.push ( attest_from ( a) ? ) ;
750251b2 Andy McCall 1 month ago ↶
1006
}
28fc23d7 Andy McCall 1 month ago ↶
1007
if let Some ( ps) = m.get ( & 3 ) {
1008
for p in o:: array( ps, " policies" ) ? {
1009
b.policies.push ( policy_from ( p) ? ) ;
f0a517d2 Andy McCall 19 days ago ↶
1010
}
1011
}
1012
if let Some ( cs) = m.get ( & 4 ) {
1013
for c in o:: array( cs, " certificates" ) ? {
1014
b.certificates.push (
1015
crate :: cert:: SignedCertificate:: decode( o:: bytes( c, " certificate" ) ? )
1016
.map_err ( | e | schema ( & format! ( " certificate: {e} " ) ) ) ? ,
1017
) ;
28fc23d7 Andy McCall 1 month ago ↶
1018
}
1019
}
750251b2 Andy McCall 1 month ago ↶
1020
Ok ( b)
1021
}
1022
1023
pub fn encode_bundle ( b : & Bundle) -> Vec< u8 > {
1024
cbor:: encode( & bundle_value ( b) )
1025
}
1026
pub fn decode_bundle ( bytes : & [u8 ]) -> Result< Bundle> {
1027
bundle_from_value ( & cbor:: decode( bytes) .map_err ( ObjectError:: from) ? )
1028
}
1029
1030
pub fn encode_capabilities ( id : [u8 ; 16]) -> Vec< u8 > {
1031
cbor:: encode( & Value:: map( [
1032
( 0 , Value:: Array( vec! [ Value:: Uint( PROTOCOL_VERSION ) ] ) ) ,
1033
( 1 , Value:: Bytes( id.to_vec ( ) ) ) ,
1034
( 2 , Value:: Array( vec! [ Value:: Text( " seal" .into ( ) ) ] ) ) ,
1035
] ) )
1036
}
1037
pub fn decode_capabilities ( bytes : & [u8 ]) -> Result< [ u8 ; 16 ] > {
1038
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1039
let m = map ( & v, " capabilities" ) ? ;
1040
let versions = o:: array( field ( m, 0 , " versions" ) ? , " versions" ) ? ;
75167672 Andy McCall 1 month ago
1041
if ! versions
1042
.iter ( )
1043
.any ( | x | x.as_uint ( ) = = Some ( PROTOCOL_VERSION ) )
1044
{
1045
return Err ( RepoError:: Msg(
1046
" remote does not speak protocol version 1" .into ( ) ,
1047
) ) ;
750251b2 Andy McCall 1 month ago ↶
1048
}
75167672 Andy McCall 1 month ago
1049
o:: bytes( field ( m, 1 , " repo id" ) ? , " repo id" ) ?
1050
.try_into ( )
1051
.map_err ( | _| schema ( " repo id: expected 16 bytes" ) )
750251b2 Andy McCall 1 month ago ↶
1052
}
1053
132b884b Andy McCall 1 month ago ↶
1054
fn tags_value ( tags : & [TagRef]) -> Value {
1055
Value:: Array(
1056
tags.iter ( )
1057
.map ( | t | {
1ec6fdbc Andy McCall 16 days ago ↶
1058
let mut v = Value:: map( [
132b884b Andy McCall 1 month ago ↶
1059
( 0 , Value:: Text( t.name.clone ( ) ) ) ,
1060
( 1 , Value:: Bytes( t.state.0. to_vec ( ) ) ) ,
1061
( 2 , hashes_value ( & t.heads) ) ,
1062
( 3 , opt_attest_value ( & t.attestation) ) ,
1ec6fdbc Andy McCall 16 days ago ↶
1063
] ) ;
1064
1065
1066
if let Value:: Map( m) = & mut v {
1067
if t.accepted.is_some ( ) {
1068
m.insert ( 4 , opt_attest_value ( & t.accepted) ) ;
1069
}
1070
if let Some ( l) = & t.logged {
1071
m.insert ( 5 , logged_value ( l) ) ;
1072
}
1073
if ! t.integration.is_empty ( ) {
1074
m.insert ( 6 , integration_value ( & t.integration) ) ;
1075
}
1076
}
1077
v
132b884b Andy McCall 1 month ago ↶
1078
} )
1079
.collect ( ) ,
1080
)
1081
}
1082
fn tags_from ( v : & Value) -> Result< Vec< TagRef> > {
1083
let mut out = Vec :: new( ) ;
1084
for t in o:: array( v, " tags" ) ? {
1085
let tm = map ( t, " tag" ) ? ;
1086
out.push ( TagRef {
1087
name: o:: text( field ( tm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1088
state: o:: oref( field ( tm, 1 , " state" ) ? , " state" ) ? ,
1089
heads: hashes_from ( field ( tm, 2 , " heads" ) ? ) ? ,
1090
attestation: opt_attest_from ( field ( tm, 3 , " attestation" ) ? ) ? ,
1ec6fdbc Andy McCall 16 days ago ↶
1091
accepted: match tm.get ( & 4 ) {
1092
Some ( a) => opt_attest_from ( a) ? ,
1093
None => None ,
1094
} ,
1095
logged: tm.get ( & 5 ) .map ( logged_from) .transpose ( ) ? ,
1096
integration: match tm.get ( & 6 ) {
1097
Some ( l) => integration_from ( l) ? ,
1098
None => Vec :: new( ) ,
1099
} ,
132b884b Andy McCall 1 month ago ↶
1100
} ) ;
1ec6fdbc Andy McCall 16 days ago ↶
1101
}
1102
Ok ( out)
1103
}
1104
1105
1106
1107
1108
# [ cfg ( test ) ]
1109
pub ( super ) fn tags_as_0_5_read_them ( bytes : & [u8 ]) -> Result< Vec< TagRef> > {
1110
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1111
let m = map ( & v, " refs" ) ? ;
1112
let mut out = Vec :: new( ) ;
1113
for t in o:: array( field ( m, 3 , " tags" ) ? , " tags" ) ? {
1114
let tm = map ( t, " tag" ) ? ;
1115
out.push ( TagRef:: new(
1116
o:: text( field ( tm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1117
o:: oref( field ( tm, 1 , " state" ) ? , " state" ) ? ,
1118
hashes_from ( field ( tm, 2 , " heads" ) ? ) ? ,
1119
opt_attest_from ( field ( tm, 3 , " attestation" ) ? ) ? ,
1120
) ) ;
132b884b Andy McCall 1 month ago ↶
1121
}
1122
Ok ( out)
1ec6fdbc Andy McCall 16 days ago ↶
1123
}
1124
1125
1126
fn logged_value ( l : & Logged) -> Value {
1127
Value:: Array( vec! [
1128
Value:: Uint( l.index) ,
1129
Value:: Bytes( l.entry.clone ( ) ) ,
1130
proof_value ( & l.proof) ,
1131
] )
132b884b Andy McCall 1 month ago ↶
1132
}
1ec6fdbc Andy McCall 16 days ago ↶
1133
fn logged_from ( v : & Value) -> Result< Logged> {
1134
let a = o:: array( v, " logged" ) ? ;
1135
if a.len ( ) ! = 3 {
1136
return Err ( schema ( " logged: expected [index, entry, proof]" ) ) ;
1137
}
1138
Ok ( Logged {
1139
index: o:: uint( & a[ 0 ] , " index" ) ? ,
1140
entry: o:: bytes( & a[ 1 ] , " entry" ) ? .to_vec ( ) ,
1141
proof: proof_from ( & a[ 2 ] ) ? ,
1142
} )
1143
}
132b884b Andy McCall 1 month ago ↶
1144
1ec6fdbc Andy McCall 16 days ago ↶
1145
1146
fn integration_value ( integration : & [Integration]) -> Value {
1147
Value:: Array(
1148
integration
1149
.iter ( )
1150
.map ( | i | Value:: Array( vec! [ Value:: Text( i.note.clone ( ) ) , proof_value ( & i.proof) ] ) )
1151
.collect ( ) ,
1152
)
1153
}
1154
fn integration_from ( v : & Value) -> Result< Vec< Integration> > {
1155
let mut out = Vec :: new( ) ;
1156
for i in o:: array( v, " integration" ) ? {
1157
let a = o:: array( i, " integration" ) ? ;
1158
if a.len ( ) ! = 2 {
1159
return Err ( schema ( " integration: expected [note, proof]" ) ) ;
1160
}
1161
out.push ( Integration {
1162
note: o:: text( & a[ 0 ] , " note" ) ? .to_owned ( ) ,
1163
proof: proof_from ( & a[ 1 ] ) ? ,
1164
} ) ;
1165
}
1166
Ok ( out)
1167
}
1168
83e0677b Andy McCall 1 month ago ↶
1169
pub fn encode_refs ( id : [u8 ; 16], resp : & RefsResponse) -> Vec< u8 > {
4b9d0677 Andy McCall 18 days ago ↶
1170
let mut v = Value:: map( [
750251b2 Andy McCall 1 month ago ↶
1171
(
1172
0 ,
1173
Value:: Array(
83e0677b Andy McCall 1 month ago ↶
1174
resp.refs
1175
.iter ( )
750251b2 Andy McCall 1 month ago ↶
1176
.map ( | r | {
f53cbe7a Andy McCall 19 days ago ↶
1177
let mut v = Value:: map( [
750251b2 Andy McCall 1 month ago ↶
1178
( 0 , Value:: Text( r.name.clone ( ) ) ) ,
1179
( 1 , Value:: Bytes( r.state.0. to_vec ( ) ) ) ,
1180
( 2 , hashes_value ( & r.heads) ) ,
1181
( 3 , opt_attest_value ( & r.attestation) ) ,
1182
( 4 , r.last_serial.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
f53cbe7a Andy McCall 19 days ago ↶
1183
] ) ;
1184
4b9d0677 Andy McCall 18 days ago ↶
1185
if let Value:: Map( m) = & mut v {
1186
if r.accepted.is_some ( ) {
f53cbe7a Andy McCall 19 days ago ↶
1187
m.insert ( 5 , opt_attest_value ( & r.accepted) ) ;
4b9d0677 Andy McCall 18 days ago ↶
1188
}
1189
if let Some ( l) = & r.logged {
1ec6fdbc Andy McCall 16 days ago ↶
1190
m.insert ( 6 , logged_value ( l) ) ;
f53cbe7a Andy McCall 19 days ago ↶
1191
}
028545df Andy McCall 17 days ago ↶
1192
if ! r.integration.is_empty ( ) {
1ec6fdbc Andy McCall 16 days ago ↶
1193
m.insert ( 7 , integration_value ( & r.integration) ) ;
028545df Andy McCall 17 days ago ↶
1194
}
f53cbe7a Andy McCall 19 days ago ↶
1195
}
1196
v
750251b2 Andy McCall 1 month ago ↶
1197
} )
1198
.collect ( ) ,
1199
) ,
1200
) ,
1201
( 1 , Value:: Bytes( id.to_vec ( ) ) ) ,
83e0677b Andy McCall 1 month ago ↶
1202
( 2 , opt_policy_value ( resp.policy.as_ref ( ) ) ) ,
132b884b Andy McCall 1 month ago ↶
1203
( 3 , tags_value ( & resp.tags) ) ,
75167672 Andy McCall 1 month ago
1204
(
1205
4 ,
1206
Value:: Array( resp.history.iter ( ) .map ( policy_value) .collect ( ) ) ,
1207
) ,
14edbaaf Andy McCall 1 month ago ↶
1208
(
1209
5 ,
1210
resp.default_bookmark
1211
.clone ( )
1212
.map ( Value:: Text)
1213
.unwrap_or ( Value:: Null) ,
1214
) ,
4b9d0677 Andy McCall 18 days ago ↶
1215
] ) ;
028545df Andy McCall 17 days ago ↶
1216
if let Value:: Map( m) = & mut v {
1217
if let Some ( log) = & resp.log {
1218
m.insert ( 6 , Value:: Text( log.clone ( ) ) ) ;
1219
}
1220
if ! resp.witnesses.is_empty ( ) {
1221
m.insert (
1222
7 ,
1223
Value:: Array(
1224
resp.witnesses
1225
.iter ( )
1226
.map ( | ( name , id ) | {
1227
Value:: Array( vec! [
1228
Value:: Text( name.clone ( ) ) ,
1229
Value:: Bytes( id.clone ( ) ) ,
1230
] )
1231
} )
1232
.collect ( ) ,
1233
) ,
1234
) ;
1235
}
4b9d0677 Andy McCall 18 days ago ↶
1236
}
1237
cbor:: encode( & v)
750251b2 Andy McCall 1 month ago ↶
1238
}
83e0677b Andy McCall 1 month ago ↶
1239
pub fn decode_refs ( bytes : & [u8 ]) -> Result< ( [u8 ; 16], RefsResponse) > {
750251b2 Andy McCall 1 month ago ↶
1240
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1241
let m = map ( & v, " refs" ) ? ;
75167672 Andy McCall 1 month ago
1242
let id: [ u8 ; 16 ] = o:: bytes( field ( m, 1 , " repo id" ) ? , " repo id" ) ?
1243
.try_into ( )
1244
.map_err ( | _| schema ( " repo id: expected 16 bytes" ) ) ? ;
750251b2 Andy McCall 1 month ago ↶
1245
let mut refs = Vec :: new( ) ;
1246
for r in o:: array( field ( m, 0 , " refs" ) ? , " refs" ) ? {
1247
let rm = map ( r, " ref" ) ? ;
1248
let ls = field ( rm, 4 , " last serial" ) ? ;
1249
refs.push ( RemoteRef {
1250
name: o:: text( field ( rm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1251
state: o:: oref( field ( rm, 1 , " state" ) ? , " state" ) ? ,
1252
heads: hashes_from ( field ( rm, 2 , " heads" ) ? ) ? ,
1253
attestation: opt_attest_from ( field ( rm, 3 , " attestation" ) ? ) ? ,
75167672 Andy McCall 1 month ago
1254
last_serial: if ls.is_null ( ) {
1255
None
1256
} else {
1257
Some ( o:: uint( ls, " last serial" ) ? )
f53cbe7a Andy McCall 19 days ago ↶
1258
} ,
1259
accepted: match rm.get ( & 5 ) {
1260
Some ( a) => opt_attest_from ( a) ? ,
1261
None => None ,
75167672 Andy McCall 1 month ago
1262
} ,
1ec6fdbc Andy McCall 16 days ago ↶
1263
logged: rm.get ( & 6 ) .map ( logged_from) .transpose ( ) ? ,
028545df Andy McCall 17 days ago ↶
1264
integration: match rm.get ( & 7 ) {
1ec6fdbc Andy McCall 16 days ago ↶
1265
Some ( l) => integration_from ( l) ? ,
028545df Andy McCall 17 days ago ↶
1266
None => Vec :: new( ) ,
1267
} ,
750251b2 Andy McCall 1 month ago ↶
1268
} ) ;
1269
}
83e0677b Andy McCall 1 month ago ↶
1270
let policy = match m.get ( & 2 ) {
1271
Some ( p) => opt_policy_from ( p) ? ,
1272
None => None ,
1273
} ;
132b884b Andy McCall 1 month ago ↶
1274
let tags = match m.get ( & 3 ) {
1275
Some ( t) => tags_from ( t) ? ,
1276
None => Vec :: new( ) ,
1277
} ;
28fc23d7 Andy McCall 1 month ago ↶
1278
let mut history = Vec :: new( ) ;
1279
if let Some ( h) = m.get ( & 4 ) {
1280
for p in o:: array( h, " history" ) ? {
1281
history.push ( policy_from ( p) ? ) ;
1282
}
1283
}
14edbaaf Andy McCall 1 month ago ↶
1284
let default_bookmark = match m.get ( & 5 ) {
4b9d0677 Andy McCall 18 days ago ↶
1285
Some ( Value:: Text( t) ) => Some ( t.clone ( ) ) ,
1286
_ => None ,
1287
} ;
1288
let log = match m.get ( & 6 ) {
14edbaaf Andy McCall 1 month ago ↶
1289
Some ( Value:: Text( t) ) => Some ( t.clone ( ) ) ,
1290
_ => None ,
1291
} ;
028545df Andy McCall 17 days ago ↶
1292
let mut witnesses = Vec :: new( ) ;
1293
if let Some ( w) = m.get ( & 7 ) {
1294
for x in o:: array( w, " witnesses" ) ? {
1295
let a = o:: array( x, " witness" ) ? ;
1296
if a.len ( ) ! = 2 {
1297
return Err ( schema ( " witness: expected [name, identity]" ) ) ;
1298
}
1299
witnesses.push ( (
1300
o:: text( & a[ 0 ] , " witness name" ) ? .to_owned ( ) ,
1301
o:: bytes( & a[ 1 ] , " witness identity" ) ? .to_vec ( ) ,
1302
) ) ;
1303
}
1304
}
75167672 Andy McCall 1 month ago
1305
Ok ( (
1306
id,
1307
RefsResponse {
1308
refs,
1309
policy,
1310
tags,
1311
history,
14edbaaf Andy McCall 1 month ago ↶
1312
default_bookmark,
4b9d0677 Andy McCall 18 days ago ↶
1313
log,
028545df Andy McCall 17 days ago ↶
1314
witnesses,
75167672 Andy McCall 1 month ago
1315
} ,
1316
) )
750251b2 Andy McCall 1 month ago ↶
1317
}
1318
028545df Andy McCall 17 days ago ↶
1319
1320
fn proof_value ( proof : & [[u8 ; 32]]) -> Value {
1321
Value:: Array( proof.iter ( ) .map ( | h | Value:: Bytes( h.to_vec ( ) ) ) .collect ( ) )
1322
}
1323
1324
fn proof_from ( v : & Value) -> Result< Vec< [ u8 ; 32 ] > > {
1325
let mut proof = Vec :: new( ) ;
1326
for h in o:: array( v, " proof" ) ? {
1327
proof.push (
1328
o:: bytes( h, " proof hash" ) ?
1329
.try_into ( )
1330
.map_err ( | _| schema ( " proof hash: expected 32 bytes" ) ) ? ,
1331
) ;
1332
}
1333
Ok ( proof)
1334
}
1335
750251b2 Andy McCall 1 month ago ↶
1336
pub fn encode_negotiate ( have : & [ChangeHash], want : & [ChangeHash]) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
1337
cbor:: encode( & Value:: map( [
1338
( 0 , hashes_value ( have) ) ,
1339
( 1 , hashes_value ( want) ) ,
1340
] ) )
750251b2 Andy McCall 1 month ago ↶
1341
}
1342
pub fn decode_negotiate ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , Vec< ChangeHash> ) > {
1343
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1344
let m = map ( & v, " negotiate" ) ? ;
75167672 Andy McCall 1 month ago
1345
Ok ( (
1346
hashes_from ( field ( m, 0 , " have" ) ? ) ? ,
1347
hashes_from ( field ( m, 1 , " want" ) ? ) ? ,
1348
) )
750251b2 Andy McCall 1 month ago ↶
1349
}
1350
b3c5a59a Andy McCall 1 month ago ↶
1351
1352
pub fn encode_fetch ( hs : & [ChangeHash], max_blob : Option< u64 > ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
1353
cbor:: encode( & Value:: map( [
1354
( 0 , hashes_value ( hs) ) ,
1355
( 1 , max_blob.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
1356
] ) )
b3c5a59a Andy McCall 1 month ago ↶
1357
}
1358
pub fn decode_fetch ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , Option< u64 > ) > {
1359
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1360
let m = map ( & v, " fetch" ) ? ;
1361
let max = match m.get ( & 1 ) {
1362
Some ( x) if ! x.is_null ( ) => Some ( o:: uint( x, " max blob" ) ? ) ,
1363
_ => None ,
1364
} ;
1365
Ok ( ( hashes_from ( field ( m, 0 , " changes" ) ? ) ? , max) )
1366
}
1367
2863456a Andy McCall 1 month ago ↶
1368
1369
pub fn encode_shallow ( hs : & [ChangeHash], depth : u32 ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
1370
cbor:: encode( & Value:: map( [
1371
( 0 , hashes_value ( hs) ) ,
1372
( 1 , Value:: Uint( depth as u64 ) ) ,
1373
] ) )
2863456a Andy McCall 1 month ago ↶
1374
}
1375
pub fn decode_shallow ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , u32 ) > {
1376
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1377
let m = map ( & v, " shallow" ) ? ;
75167672 Andy McCall 1 month ago
1378
Ok ( (
1379
hashes_from ( field ( m, 0 , " heads" ) ? ) ? ,
1380
o:: u32_( field ( m, 1 , " depth" ) ? , " depth" ) ? ,
1381
) )
2863456a Andy McCall 1 month ago ↶
1382
}
1383
750251b2 Andy McCall 1 month ago ↶
1384
pub fn encode_hashes ( hs : & [ChangeHash]) -> Vec< u8 > {
1385
cbor:: encode( & Value:: map( [ ( 0 , hashes_value ( hs) ) ] ) )
1386
}
1387
pub fn decode_hashes ( bytes : & [u8 ]) -> Result< Vec< ChangeHash> > {
1388
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1389
hashes_from ( field ( map ( & v, " hashes" ) ? , 0 , " hashes" ) ? )
1390
}
1391
75167672 Andy McCall 1 month ago
1392
pub fn encode_push (
1393
bundle : & Bundle,
1394
updates : & [RefUpdate],
1395
policy : Option< & SignedPolicy> ,
1396
tags : & [TagRef],
1397
) -> Vec< u8 > {
750251b2 Andy McCall 1 month ago ↶
1398
cbor:: encode( & Value:: map( [
1399
( 0 , bundle_value ( bundle) ) ,
83e0677b Andy McCall 1 month ago ↶
1400
( 2 , opt_policy_value ( policy) ) ,
132b884b Andy McCall 1 month ago ↶
1401
( 3 , tags_value ( tags) ) ,
750251b2 Andy McCall 1 month ago ↶
1402
(
1403
1 ,
1404
Value:: Array(
1405
updates
1406
.iter ( )
1407
.map ( | u | {
1408
Value:: map( [
1409
( 0 , Value:: Text( u.name.clone ( ) ) ) ,
1410
( 1 , Value:: Bytes( u.old_state.0. to_vec ( ) ) ) ,
1411
( 2 , Value:: Bytes( u.new_state.0. to_vec ( ) ) ) ,
1412
( 3 , Value:: Bool( u.force) ) ,
1413
( 4 , opt_attest_value ( & u.attestation) ) ,
1414
] )
1415
} )
1416
.collect ( ) ,
1417
) ,
1418
) ,
1419
] ) )
1420
}
132b884b Andy McCall 1 month ago ↶
1421
1422
pub type PushParts = ( Bundle, Vec< RefUpdate> , Option< SignedPolicy> , Vec< TagRef> ) ;
1423
1424
pub fn decode_push ( bytes : & [u8 ]) -> Result< PushParts> {
750251b2 Andy McCall 1 month ago ↶
1425
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1426
let m = map ( & v, " push" ) ? ;
1427
let bundle = bundle_from_value ( field ( m, 0 , " bundle" ) ? ) ? ;
1428
let mut updates = Vec :: new( ) ;
1429
for u in o:: array( field ( m, 1 , " updates" ) ? , " updates" ) ? {
1430
let um = map ( u, " update" ) ? ;
1431
updates.push ( RefUpdate {
1432
name: o:: text( field ( um, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1433
old_state: o:: oref( field ( um, 1 , " old_state" ) ? , " old_state" ) ? ,
1434
new_state: o:: oref( field ( um, 2 , " new_state" ) ? , " new_state" ) ? ,
75167672 Andy McCall 1 month ago
1435
force: field ( um, 3 , " force" ) ?
1436
.as_bool ( )
1437
.ok_or_else ( | | schema ( " force: expected bool" ) ) ? ,
750251b2 Andy McCall 1 month ago ↶
1438
attestation: opt_attest_from ( field ( um, 4 , " attestation" ) ? ) ? ,
1439
} ) ;
1440
}
83e0677b Andy McCall 1 month ago ↶
1441
let policy = match m.get ( & 2 ) {
1442
Some ( p) => opt_policy_from ( p) ? ,
1443
None => None ,
1444
} ;
132b884b Andy McCall 1 month ago ↶
1445
let tags = match m.get ( & 3 ) {
1446
Some ( t) => tags_from ( t) ? ,
1447
None => Vec :: new( ) ,
1448
} ;
1449
Ok ( ( bundle, updates, policy, tags) )
750251b2 Andy McCall 1 month ago ↶
1450
}
1451
1452
pub fn encode_push_response ( results : & [RefResult]) -> Vec< u8 > {
1453
cbor:: encode( & Value:: map( [ (
1454
0 ,
1455
Value:: Array(
1456
results
1457
.iter ( )
75167672 Andy McCall 1 month ago
1458
.map ( | r | {
1459
Value:: map( [
1460
( 0 , Value:: Text( r.name.clone ( ) ) ) ,
1461
( 1 , Value:: Bool( r.ok) ) ,
1462
( 2 , Value:: Text( r.reason.clone ( ) ) ) ,
1463
] )
1464
} )
750251b2 Andy McCall 1 month ago ↶
1465
.collect ( ) ,
1466
) ,
1467
) ] ) )
1468
}
1469
pub fn decode_push_response ( bytes : & [u8 ]) -> Result< Vec< RefResult> > {
1470
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1471
let m = map ( & v, " push response" ) ? ;
1472
let mut out = Vec :: new( ) ;
1473
for r in o:: array( field ( m, 0 , " results" ) ? , " results" ) ? {
1474
let rm = map ( r, " result" ) ? ;
1475
out.push ( RefResult {
1476
name: o:: text( field ( rm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
75167672 Andy McCall 1 month ago
1477
ok: field ( rm, 1 , " ok" ) ?
1478
.as_bool ( )
1479
.ok_or_else ( | | schema ( " ok: expected bool" ) ) ? ,
750251b2 Andy McCall 1 month ago ↶
1480
reason: o:: text( field ( rm, 2 , " reason" ) ? , " reason" ) ? .to_owned ( ) ,
1481
} ) ;
1482
}
1483
Ok ( out)
1484
}
1485
}
1486
1487
# [ cfg ( test ) ]
1488
mod tests {
1489
use super :: * ;
1490
use crate :: attest:: AttestBody;
1491
use crate :: objects:: Hlc;
d3b27090 Andy McCall 11 days ago ↶
1492
1493
1494
1495
# [ test ]
1496
fn html_error_pages_are_shown_by_their_title ( ) {
1497
let cloudflare = " <html>\r \n <head><title>413 Request Entity Too Large</title></head>\r \n <body><center><h1>413</h1></center><hr><center>cloudflare</center></body></html>" ;
1498
assert_eq! (
1499
plain_error_text ( cloudflare) ,
1500
" 413 Request Entity Too Large (an HTML error page, from a proxy in front of the server perhaps)"
1501
) ;
1502
assert_eq! (
1503
plain_error_text ( " refused: a secret\n at a.txt:3 " ) ,
1504
" refused: a secret\n at a.txt:3"
1505
) ;
1506
assert! ( hint_for ( " https://hub/x: HTTP 413: too large" )
1507
.unwrap ( )
1508
.contains ( " push.batch-bytes" ) ) ;
1509
}
ee130dd9 Andy McCall 16 days ago ↶
1510
1511
# [ test ]
1512
fn client_features_are_declared_and_read ( ) {
1513
assert_eq! ( CLIENT_FEATURES .join ( " ," ) , " certified-tags" ) ;
1514
assert! ( user_agent ( ) .starts_with ( " oxy/" ) ) ;
1515
assert! ( declares_feature (
1516
Some ( " certified-tags" ) ,
1517
FEATURE_CERTIFIED_TAGS
1518
) ) ;
1519
assert! ( declares_feature (
1520
Some ( " later-thing, Certified-Tags ,x" ) ,
1521
FEATURE_CERTIFIED_TAGS
1522
) ) ;
1523
assert! ( ! declares_feature (
1524
Some ( " certified-tags-v2" ) ,
1525
FEATURE_CERTIFIED_TAGS
1526
) ) ;
1527
assert! ( ! declares_feature ( Some ( " " ) , FEATURE_CERTIFIED_TAGS ) ) ;
1528
assert! ( ! declares_feature ( None , FEATURE_CERTIFIED_TAGS ) ) ;
1529
}
750251b2 Andy McCall 1 month ago ↶
1530
1531
# [ test ]
1532
fn wire_round_trips ( ) {
1533
let c = crate :: objects:: tests:: sample_change( ) ;
75167672 Andy McCall 1 month ago
1534
let sk = crate :: identity:: SecretKey {
1535
ed_seed: [ 1 ; 32 ] ,
1536
ml_seed: [ 2 ; 32 ] ,
1537
} ;
750251b2 Andy McCall 1 month ago ↶
1538
let id = sk.identity ( " a" , " b" , 0 ) ;
1539
let key = id.hash ( ) ;
1540
let sig = sk.sign ( crate :: identity:: Domain:: Change, & key, & c.hash ( ) ) ;
1541
let seal = Attest:: seal( [ 3 ; 16 ] , vec! [ c.hash ( ) ] ) ;
1542
let seal_sig = sk.sign ( crate :: identity:: Domain:: Attest, & key, & seal.hash ( ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
1543
let policy = Policy:: single_root( [ 3 ; 16 ] , id.clone ( ) , 99 ) ;
1544
let psig = sk.sign ( crate :: identity:: Domain:: Policy, & key, & policy.hash ( ) ) ;
750251b2 Andy McCall 1 month ago ↶
1545
let bundle = Bundle {
1546
changes: vec! [ ( c.clone ( ) , vec! [ sig.clone ( ) ] ) ] ,
75167672 Andy McCall 1 month ago
1547
attests: vec! [ SignedAttest {
1548
attest: seal.clone ( ) ,
1549
sigs: vec! [ seal_sig] ,
1550
} ] ,
1551
objects: vec! [
1552
( ObjectType:: Identity, id.encode ( ) ) ,
1553
( ObjectType:: Chunk, b " xyz" .to_vec ( ) ) ,
1554
] ,
1555
policies: vec! [ SignedPolicy {
1556
policy: policy.clone ( ) ,
1557
sigs: vec! [ psig.clone ( ) ] ,
1558
} ] ,
598009a7 Andy McCall 1 month ago ↶
1559
hashes: vec! [ ] ,
2c75672f Andy McCall 1 month ago ↶
1560
raw_changes: vec! [ ] ,
f0a517d2 Andy McCall 19 days ago ↶
1561
certificates: vec! [ ] ,
750251b2 Andy McCall 1 month ago ↶
1562
} ;
1563
let d = wire:: decode_bundle( & wire:: encode_bundle( & bundle) ) .unwrap ( ) ;
598009a7 Andy McCall 1 month ago ↶
1564
assert_eq! (
1565
d.hashes,
1566
vec! [ c.hash ( ) ] ,
1567
" the receiver hashes what it was sent"
1568
) ;
750251b2 Andy McCall 1 month ago ↶
1569
assert_eq! ( d.changes, bundle.changes) ;
1570
assert_eq! ( d.attests, bundle.attests) ;
1571
assert_eq! ( d.objects, bundle.objects) ;
28fc23d7 Andy McCall 1 month ago ↶
1572
assert_eq! ( d.policies, bundle.policies) ;
f0a517d2 Andy McCall 19 days ago ↶
1573
assert! ( d.certificates.is_empty ( ) ) ;
1574
1575
1576
let plain = wire:: encode_bundle( & bundle) ;
1577
let mut with = bundle.clone ( ) ;
1578
let issuer_sk = crate :: identity:: SecretKey:: generate( ) ;
1579
let issuer = issuer_sk.identity ( " hub" , " h@x" , 1 ) ;
1580
with.certificates.push ( crate :: cert:: SignedCertificate:: sign(
1581
crate :: cert:: Certificate {
1582
key: id.clone ( ) ,
1583
issuer: issuer.hash ( ) ,
1584
subject: " agent:a@b" .into ( ) ,
1585
claims: vec! [ ] ,
1586
scope: crate :: identity:: Scope:: CHANGES ,
1587
not_before: 1 ,
1588
expires: 2 ,
a2387d54 Andy McCall 18 days ago ↶
1589
grant: None ,
f0a517d2 Andy McCall 19 days ago ↶
1590
} ,
1591
& issuer,
1592
& issuer_sk,
1593
) ) ;
1594
let encoded = wire:: encode_bundle( & with) ;
1595
assert_ne! ( encoded, plain) ;
1596
assert_eq! (
1597
wire:: decode_bundle( & encoded) .unwrap ( ) .certificates,
1598
with.certificates
1599
) ;
1600
with.certificates.clear ( ) ;
1601
assert_eq! ( wire:: encode_bundle( & with) , plain) ;
750251b2 Andy McCall 1 month ago ↶
1602
75167672 Andy McCall 1 month ago
1603
let att = Attest {
1604
repo: [ 3 ; 16 ] ,
1605
hlc: Hlc:: default( ) ,
1606
body: AttestBody:: Ref {
1607
name: " main" .into ( ) ,
1608
old_state: ObjectRef( [ 1 ; 32 ] ) ,
1609
new_state: ObjectRef( [ 2 ; 32 ] ) ,
1610
serial: 4 ,
1611
} ,
1612
} ;
750251b2 Andy McCall 1 month ago ↶
1613
let refs = vec! [
75167672 Andy McCall 1 month ago
1614
RemoteRef {
1615
name: " main" .into ( ) ,
1616
state: ObjectRef( [ 2 ; 32 ] ) ,
1617
heads: vec! [ c.hash ( ) ] ,
1618
attestation: Some ( SignedAttest {
1619
attest: att.clone ( ) ,
1620
sigs: vec! [ sig.clone ( ) ] ,
1621
} ) ,
1622
last_serial: Some ( 4 ) ,
f53cbe7a Andy McCall 19 days ago ↶
1623
accepted: None ,
4b9d0677 Andy McCall 18 days ago ↶
1624
logged: None ,
028545df Andy McCall 17 days ago ↶
1625
integration: vec! [ ] ,
75167672 Andy McCall 1 month ago
1626
} ,
1627
RemoteRef {
1628
name: " dev" .into ( ) ,
1629
state: ObjectRef( [ 5 ; 32 ] ) ,
1630
heads: vec! [ ] ,
1631
attestation: None ,
1632
last_serial: None ,
f53cbe7a Andy McCall 19 days ago ↶
1633
accepted: None ,
4b9d0677 Andy McCall 18 days ago ↶
1634
logged: None ,
028545df Andy McCall 17 days ago ↶
1635
integration: vec! [ ] ,
75167672 Andy McCall 1 month ago
1636
} ,
750251b2 Andy McCall 1 month ago ↶
1637
] ;
1ec6fdbc Andy McCall 16 days ago ↶
1638
let tag = TagRef:: new( " v1" .into ( ) , ObjectRef( [ 2 ; 32 ] ) , vec! [ c.hash ( ) ] , None ) ;
4b9d0677 Andy McCall 18 days ago ↶
1639
let mut resp = RefsResponse {
75167672 Andy McCall 1 month ago
1640
refs,
1641
policy: Some ( SignedPolicy {
1642
policy: policy.clone ( ) ,
1643
sigs: vec! [ psig.clone ( ) ] ,
1644
} ) ,
1645
tags: vec! [ tag.clone ( ) ] ,
1646
history: vec! [ SignedPolicy {
1647
policy: policy.clone ( ) ,
1648
sigs: vec! [ ] ,
1649
} ] ,
14edbaaf Andy McCall 1 month ago ↶
1650
default_bookmark: Some ( " develop" .into ( ) ) ,
4b9d0677 Andy McCall 18 days ago ↶
1651
log: Some ( " hub.example/log\n 2\n root\n \n - sig\n " .into ( ) ) ,
028545df Andy McCall 17 days ago ↶
1652
witnesses: vec! [ ] ,
75167672 Andy McCall 1 month ago
1653
} ;
028545df Andy McCall 17 days ago ↶
1654
1655
1656
let old = wire:: encode_refs( [ 3 ; 16 ] , & resp) ;
1657
let back = wire:: decode_refs( & old) .unwrap ( ) .1 ;
1658
assert! ( back.witnesses.is_empty ( ) & & back.refs.iter ( ) .all ( | r | r.integration.is_empty ( ) ) ) ;
4b9d0677 Andy McCall 18 days ago ↶
1659
resp.refs[ 1 ] .logged = Some ( Logged {
1660
index: 41 ,
1661
entry: vec! [ 1 , 2 , 3 ] ,
1662
proof: vec! [ [ 9 ; 32 ] , [ 8 ; 32 ] ] ,
1663
} ) ;
028545df Andy McCall 17 days ago ↶
1664
resp.refs[ 1 ] .integration = vec! [ Integration {
1665
note: " hub.example/log\n 42\n root\n \n - sig\n " .into ( ) ,
1666
proof: vec! [ [ 7 ; 32 ] ] ,
1667
} ] ;
1668
resp.witnesses = vec! [ ( " w.example" .into ( ) , vec! [ 4 , 5 , 6 ] ) ] ;
1669
let new = wire:: encode_refs( [ 3 ; 16 ] , & resp) ;
1670
let ( id_back, back) = wire:: decode_refs( & new) .unwrap ( ) ;
4b9d0677 Andy McCall 18 days ago ↶
1671
assert_eq! ( back.log, resp.log) ;
1672
assert_eq! ( back.refs[ 1 ] .logged, resp.refs[ 1 ] .logged) ;
028545df Andy McCall 17 days ago ↶
1673
assert_eq! ( back.refs[ 1 ] .integration, resp.refs[ 1 ] .integration) ;
1674
assert_eq! ( back.witnesses, resp.witnesses) ;
4b9d0677 Andy McCall 18 days ago ↶
1675
assert_eq! ( back.refs[ 0 ] .logged, None ) ;
028545df Andy McCall 17 days ago ↶
1676
1677
let crate :: cbor:: Value:: Map( m) = crate :: cbor:: decode( & new) .unwrap ( ) else {
1678
panic! ( )
1679
} ;
1680
assert! ( m.contains_key ( & 7 ) ) ;
750251b2 Andy McCall 1 month ago ↶
1681
assert_eq! ( id_back, [ 3 ; 16 ] ) ;
83e0677b Andy McCall 1 month ago ↶
1682
assert_eq! ( back.refs.len ( ) , 2 ) ;
1683
assert_eq! ( back.refs[ 0 ] .attestation.as_ref ( ) .unwrap ( ) .attest, att) ;
1684
assert_eq! ( back.refs[ 1 ] .last_serial, None ) ;
1685
assert_eq! ( back.policy.as_ref ( ) .unwrap ( ) .policy, policy) ;
28fc23d7 Andy McCall 1 month ago ↶
1686
assert_eq! ( back.history, resp.history) ;
132b884b Andy McCall 1 month ago ↶
1687
assert_eq! ( back.tags, vec! [ tag.clone ( ) ] ) ;
1ec6fdbc Andy McCall 16 days ago ↶
1688
1689
1690
1691
let crate :: cbor:: Value:: Map( m) = crate :: cbor:: decode( & new) .unwrap ( ) else {
1692
panic! ( )
1693
} ;
1694
let crate :: cbor:: Value:: Array( tags) = & m[ & 3 ] else {
1695
panic! ( )
1696
} ;
1697
let crate :: cbor:: Value:: Map( plain) = & tags[ 0 ] else {
1698
panic! ( )
1699
} ;
1700
assert_eq! ( plain.keys ( ) .copied ( ) .collect:: < Vec< _ > > ( ) , vec! [ 0 , 1 , 2 , 3 ] ) ;
1701
let mut full = tag.clone ( ) ;
1702
full.name = " v2" .into ( ) ;
1703
full.attestation = Some ( SignedAttest {
1704
attest: att.clone ( ) ,
1705
sigs: vec! [ sig.clone ( ) ] ,
1706
} ) ;
1707
full.accepted = full.attestation.clone ( ) ;
1708
full.logged = Some ( Logged {
1709
index: 7 ,
1710
entry: vec! [ 4 ] ,
1711
proof: vec! [ [ 3 ; 32 ] ] ,
1712
} ) ;
1713
full.integration = vec! [ Integration {
1714
note: " hub.example/log\n 8\n root\n \n - sig\n " .into ( ) ,
1715
proof: vec! [ ] ,
1716
} ] ;
1717
resp.tags = vec! [ full.clone ( ) , tag.clone ( ) ] ;
1718
let bytes = wire:: encode_refs( [ 3 ; 16 ] , & resp) ;
1719
assert_eq! ( wire:: decode_refs( & bytes) .unwrap ( ) .1. tags, resp.tags) ;
1720
1721
let pushed = wire:: encode_push( & Bundle:: default( ) , & [ ] , None , & resp.tags) ;
1722
assert_eq! ( wire:: decode_push( & pushed) .unwrap ( ) .3 , resp.tags) ;
1723
1724
1725
let stripped: Vec< TagRef> = resp
1726
.tags
1727
.iter ( )
1728
.map ( | t | {
1729
TagRef:: new(
1730
t.name.clone ( ) ,
1731
t.state,
1732
t.heads.clone ( ) ,
1733
t.attestation.clone ( ) ,
1734
)
1735
} )
1736
.collect ( ) ;
1737
assert_eq! ( wire:: tags_as_0_5_read_them( & bytes) .unwrap ( ) , stripped) ;
750251b2 Andy McCall 1 month ago ↶
1738
75167672 Andy McCall 1 month ago
1739
let ( h, w) = wire:: decode_negotiate( & wire:: encode_negotiate(
1740
& [ ObjectRef( [ 1 ; 32 ] ) ] ,
1741
& [ ObjectRef( [ 2 ; 32 ] ) ] ,
1742
) )
1743
.unwrap ( ) ;
750251b2 Andy McCall 1 month ago ↶
1744
assert_eq! ( ( h, w) , ( vec! [ ObjectRef( [ 1 ; 32 ] ) ] , vec! [ ObjectRef( [ 2 ; 32 ] ) ] ) ) ;
75167672 Andy McCall 1 month ago
1745
assert_eq! (
1746
wire:: decode_hashes( & wire:: encode_hashes( & [ c.hash ( ) ] ) ) .unwrap ( ) ,
1747
vec! [ c.hash ( ) ]
1748
) ;
1749
assert_eq! (
1750
wire:: decode_fetch( & wire:: encode_fetch( & [ c.hash ( ) ] , Some ( 4096 ) ) ) .unwrap ( ) ,
1751
( vec! [ c.hash ( ) ] , Some ( 4096 ) )
1752
) ;
1753
assert_eq! (
1754
wire:: decode_fetch( & wire:: encode_fetch( & [ ] , None ) ) .unwrap ( ) ,
1755
( vec! [ ] , None )
1756
) ;
1757
assert_eq! (
1758
wire:: decode_shallow( & wire:: encode_shallow( & [ c.hash ( ) ] , 3 ) ) .unwrap ( ) ,
1759
( vec! [ c.hash ( ) ] , 3 )
1760
) ;
b3c5a59a Andy McCall 1 month ago ↶
1761
75167672 Andy McCall 1 month ago
1762
assert_eq! (
1763
wire:: decode_fetch( & wire:: encode_hashes( & [ c.hash ( ) ] ) ) .unwrap ( ) ,
1764
( vec! [ c.hash ( ) ] , None )
1765
) ;
750251b2 Andy McCall 1 month ago ↶
1766
75167672 Andy McCall 1 month ago
1767
let up = RefUpdate {
1768
name: " main" .into ( ) ,
1769
old_state: ObjectRef( [ 1 ; 32 ] ) ,
1770
new_state: ObjectRef( [ 2 ; 32 ] ) ,
1771
force: true ,
1772
attestation: Some ( SignedAttest {
1773
attest: att,
1774
sigs: vec! [ sig] ,
1775
} ) ,
1776
} ;
1777
let sp = SignedPolicy {
1778
policy,
1779
sigs: vec! [ psig] ,
1780
} ;
1781
let ( b2, ups, pol, tags) = wire:: decode_push( & wire:: encode_push(
1782
& bundle,
1783
std:: slice:: from_ref( & up) ,
1784
Some ( & sp) ,
1785
std:: slice:: from_ref( & tag) ,
1786
) )
1787
.unwrap ( ) ;
83e0677b Andy McCall 1 month ago ↶
1788
assert_eq! ( pol, Some ( sp) ) ;
132b884b Andy McCall 1 month ago ↶
1789
assert_eq! ( tags, vec! [ tag] ) ;
750251b2 Andy McCall 1 month ago ↶
1790
assert_eq! ( b2.changes.len ( ) , 1 ) ;
1791
assert_eq! ( ups[ 0 ] .name, " main" ) ;
1792
assert! ( ups[ 0 ] .force) ;
1793
assert_eq! ( ups[ 0 ] .attestation, up.attestation) ;
75167672 Andy McCall 1 month ago
1794
let res = vec! [ RefResult {
1795
name: " main" .into ( ) ,
1796
ok: false ,
1797
reason: " stale" .into ( ) ,
1798
} ] ;
1799
assert_eq! (
1800
wire:: decode_push_response( & wire:: encode_push_response( & res) ) .unwrap ( ) ,
1801
res
1802
) ;
1803
assert_eq! (
1804
wire:: decode_capabilities( & wire:: encode_capabilities( [ 7 ; 16 ] ) ) .unwrap ( ) ,
1805
[ 7 ; 16 ]
1806
) ;
750251b2 Andy McCall 1 month ago ↶
1807
}
1808
}