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> ,
83e0677b Andy McCall 1 month ago ↶
59
}
60
132b884b Andy McCall 1 month ago ↶
61
62
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
63
pub struct TagRef {
64
pub name : String,
65
pub state : ObjectRef,
66
pub heads : Vec< ChangeHash> ,
67
pub attestation : Option< SignedAttest> ,
68
}
69
5b9c6ad4 Andy McCall 1 month ago ↶
70
71
# [ derive ( Clone, Debug ) ]
72
pub struct RemoteRef {
73
pub name : String,
74
pub state : ObjectRef,
75
pub heads : Vec< ChangeHash> ,
76
pub attestation : Option< SignedAttest> ,
77
}
78
79
80
# [ derive ( Clone, Debug, Default ) ]
81
pub struct Bundle {
82
83
pub changes : Vec< ( Change, Vec< Signature> ) > ,
84
85
pub attests : Vec< SignedAttest> ,
86
87
pub objects : Vec< ( ObjectType, Vec< u8 > ) > ,
28fc23d7 Andy McCall 1 month ago ↶
88
89
90
pub policies : Vec< SignedPolicy> ,
598009a7 Andy McCall 1 month ago ↶
91
92
93
94
pub hashes : Vec< ChangeHash> ,
2c75672f Andy McCall 1 month ago ↶
95
96
97
98
pub raw_changes : Vec< ( Vec< u8 > , Vec< Signature> ) > ,
5b9c6ad4 Andy McCall 1 month ago ↶
99
}
100
101
impl Bundle {
102
pub fn is_empty ( & self ) -> bool {
103
self .changes.is_empty ( ) & & self .attests.is_empty ( ) & & self .objects.is_empty ( )
104
}
105
}
106
107
108
# [ derive ( Clone, Debug ) ]
109
pub struct RefUpdate {
110
pub name : String,
111
pub old_state : ObjectRef,
112
pub new_state : ObjectRef,
113
pub force : bool ,
114
pub attestation : Option< SignedAttest> ,
115
}
116
117
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
118
pub struct RefResult {
119
pub name : String,
120
pub ok : bool ,
121
pub reason : String,
122
}
123
124
pub trait Remote {
125
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > ;
83e0677b Andy McCall 1 month ago ↶
126
fn refs ( & self ) -> Result< RefsResponse> ;
5b9c6ad4 Andy McCall 1 month ago ↶
127
128
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > ;
b3c5a59a Andy McCall 1 month ago ↶
129
130
131
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> ;
132
133
134
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > ;
2863456a Andy McCall 1 month ago ↶
135
136
137
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> ;
138
139
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > ;
132b884b Andy McCall 1 month ago ↶
140
141
142
75167672 Andy McCall 1 month ago
143
fn push (
144
& self ,
145
bundle : Bundle,
146
updates : Vec< RefUpdate> ,
147
policy : Option< SignedPolicy> ,
148
tags : Vec< TagRef> ,
149
) -> Result< Vec< RefResult> > ;
5b9c6ad4 Andy McCall 1 month ago ↶
150
}
151
750251b2 Andy McCall 1 month ago ↶
152
153
5b9c6ad4 Andy McCall 1 month ago ↶
154
155
pub struct LocalRemote {
156
pub repo : Repo,
84b9e9ac Andy McCall 1 month ago ↶
157
158
159
pub claimed_id : Option< [ u8 ; 16 ] > ,
5b9c6ad4 Andy McCall 1 month ago ↶
160
}
161
162
impl LocalRemote {
163
pub fn open ( path : & Path) -> Result< LocalRemote> {
75167672 Andy McCall 1 month ago
164
Ok ( LocalRemote {
165
repo: Repo:: open( path) ? ,
84b9e9ac Andy McCall 1 month ago ↶
166
claimed_id: None ,
75167672 Andy McCall 1 month ago
167
} )
5b9c6ad4 Andy McCall 1 month ago ↶
168
}
169
}
170
171
impl Remote for LocalRemote {
172
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > {
173
self .repo.id ( )
174
}
83e0677b Andy McCall 1 month ago ↶
175
fn refs ( & self ) -> Result< RefsResponse> {
5b9c6ad4 Andy McCall 1 month ago ↶
176
self .repo.remote_refs ( )
177
}
178
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
179
self .repo.negotiate ( have, want)
180
}
b3c5a59a Andy McCall 1 month ago ↶
181
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
2c75672f Andy McCall 1 month ago ↶
182
self .repo.bundle_raw_for ( changes, max_blob)
b3c5a59a Andy McCall 1 month ago ↶
183
}
184
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
185
self .repo.objects_for ( refs)
5b9c6ad4 Andy McCall 1 month ago ↶
186
}
2863456a Andy McCall 1 month ago ↶
187
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
188
self .repo.checkpoint_bundle_for ( heads)
189
}
190
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
191
self .repo.shallow_changes ( heads, depth)
192
}
75167672 Andy McCall 1 month ago
193
fn push (
194
& self ,
195
bundle : Bundle,
196
updates : Vec< RefUpdate> ,
197
policy : Option< SignedPolicy> ,
198
tags : Vec< TagRef> ,
199
) -> Result< Vec< RefResult> > {
84b9e9ac Andy McCall 1 month ago ↶
200
if self .repo.is_unbound ( ) {
201
match self .claimed_id {
202
Some ( id) => self .repo.bind_id ( id) ? ,
eae59556 Andy McCall 1 month ago ↶
203
None => {
204
return Err ( RepoError:: Msg(
205
" the remote is unbound and this client did not say which repository it is"
206
.into ( ) ,
207
) )
208
}
84b9e9ac Andy McCall 1 month ago ↶
209
}
210
}
132b884b Andy McCall 1 month ago ↶
211
self .repo.receive_push ( bundle, updates, policy, tags)
5b9c6ad4 Andy McCall 1 month ago ↶
212
}
213
}
214
750251b2 Andy McCall 1 month ago ↶
215
216
2b5ee371 Andy McCall 1 month ago ↶
217
218
# [ derive ( Clone, Debug, Default ) ]
219
pub struct RemoteOptions {
220
221
pub token : Option< String> ,
222
223
224
pub ca : Option< PathBuf> ,
75167672 Andy McCall 1 month ago
225
226
227
pub repo_id : Option< [ u8 ; 16 ] > ,
9f9e72b9 Andy McCall 1 month ago ↶
228
229
230
231
232
pub cache_dir : Option< PathBuf> ,
68a92484 Andy McCall 24 days ago ↶
233
234
235
236
237
pub fallback_tokens : Vec< String> ,
2b5ee371 Andy McCall 1 month ago ↶
238
}
239
9f9e72b9 Andy McCall 1 month ago ↶
240
241
242
243
pub type StoreOpener =
244
fn ( & str , & RemoteOptions) -> Option< crate:: strata:: Result< Box< dyn crate:: strata:: Store> > > ;
245
246
static STORE_OPENERS : Mutex< Vec< StoreOpener> > = Mutex:: new( Vec :: new( ) ) ;
247
248
pub fn register_store_opener ( opener : StoreOpener) {
249
STORE_OPENERS
250
.lock ( )
251
.unwrap_or_else ( | p | p.into_inner ( ) )
252
.push ( opener) ;
253
}
254
255
256
pub fn open_store (
257
url : & str ,
258
options : & RemoteOptions,
259
) -> Option< crate:: strata:: Result< Box< dyn crate:: strata:: Store> > > {
260
if let Some ( path) = url.strip_prefix ( " dir://" ) {
261
return Some (
262
crate :: strata:: LocalStore:: open( path)
263
.map ( | s | Box :: new( s) as Box< dyn crate:: strata:: Store> ) ,
264
) ;
265
}
266
let openers = STORE_OPENERS .lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) ;
267
openers.iter ( ) .find_map ( | open | open ( url, options) )
268
}
269
75167672 Andy McCall 1 month ago
270
271
pub const REPO_ID_HEADER : & str = " Oxy-Repo-Id" ;
272
273
274
pub const UNBOUND_ID : [ u8 ; 16 ] = [ 0 ; 16 ] ;
275
750251b2 Andy McCall 1 month ago ↶
276
277
pub struct HttpRemote {
278
base : String,
68a92484 Andy McCall 24 days ago ↶
279
280
token : Mutex< Option< String> > ,
281
fallbacks : Mutex< Vec< String> > ,
75167672 Andy McCall 1 month ago
282
repo_id : Option< [ u8 ; 16 ] > ,
750251b2 Andy McCall 1 month ago ↶
283
agent : ureq:: Agent,
284
}
285
2b5ee371 Andy McCall 1 month ago ↶
286
287
fn tls_client_config ( ca : Option< & Path> ) -> Result< Arc< rustls:: ClientConfig> > {
288
let mut roots = rustls:: RootCertStore:: empty( ) ;
289
roots.extend ( webpki_roots:: TLS_SERVER_ROOTS .iter ( ) .cloned ( ) ) ;
290
if let Some ( path) = ca {
75167672 Andy McCall 1 month ago
291
let pem = std:: fs:: read( path)
292
.map_err ( | e | RepoError:: Msg( format! ( " reading CA file {} : {e} " , path.display ( ) ) ) ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
293
let certs = rustls_pemfile:: certs( & mut pem.as_slice ( ) )
294
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( )
295
.map_err ( | e | RepoError:: Msg( format! ( " parsing CA file {} : {e} " , path.display ( ) ) ) ) ? ;
296
if certs.is_empty ( ) {
75167672 Andy McCall 1 month ago
297
return Err ( RepoError:: Msg( format! (
298
" CA file {} holds no certificates" ,
299
path.display ( )
300
) ) ) ;
2b5ee371 Andy McCall 1 month ago ↶
301
}
302
for c in certs {
75167672 Andy McCall 1 month ago
303
roots
304
.add ( c)
305
.map_err ( | e | RepoError:: Msg( format! ( " CA file {} : {e} " , path.display ( ) ) ) ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
306
}
307
}
75167672 Andy McCall 1 month ago
308
let cfg = rustls:: ClientConfig:: builder_with_provider( Arc:: new(
309
rustls:: crypto:: ring:: default_provider( ) ,
310
) )
311
.with_safe_default_protocol_versions ( )
312
.map_err ( | e | RepoError:: Msg( e.to_string ( ) ) ) ?
313
.with_root_certificates ( roots)
314
.with_no_client_auth ( ) ;
2b5ee371 Andy McCall 1 month ago ↶
315
Ok ( Arc:: new( cfg) )
316
}
317
750251b2 Andy McCall 1 month ago ↶
318
impl HttpRemote {
2b5ee371 Andy McCall 1 month ago ↶
319
pub fn new ( base : & str , options : & RemoteOptions) -> Result< HttpRemote> {
ff1f62fe Andy McCall 1 month ago ↶
320
refuse_plain_token ( base, options) ? ;
75167672 Andy McCall 1 month ago
321
let agent = ureq:: AgentBuilder:: new( )
322
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
323
.build ( ) ;
324
Ok ( HttpRemote {
325
base: base.trim_end_matches ( ' /' ) .to_string ( ) ,
68a92484 Andy McCall 24 days ago ↶
326
token: Mutex:: new( options.token.clone ( ) ) ,
327
fallbacks: Mutex:: new( options.fallback_tokens.iter ( ) .rev ( ) .cloned ( ) .collect ( ) ) ,
75167672 Andy McCall 1 month ago
328
repo_id: options.repo_id,
329
agent,
330
} )
750251b2 Andy McCall 1 month ago ↶
331
}
332
68a92484 Andy McCall 24 days ago ↶
333
334
750251b2 Andy McCall 1 month ago ↶
335
fn call ( & self , post : bool , endpoint : & str , body : Option< & [ u8 ] > ) -> Result< Vec< u8 > > {
68a92484 Andy McCall 24 days ago ↶
336
loop {
337
let token = self .token.lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) .clone ( ) ;
338
match self .call_once ( post, endpoint, body, token.as_deref ( ) ) {
339
Err ( RepoError:: Msg( m) ) if token.is_some ( ) & & refused ( & m) => {
340
let next = self
341
.fallbacks
342
.lock ( )
343
.unwrap_or_else ( | p | p.into_inner ( ) )
344
.pop ( ) ;
345
let Some ( next) = next else {
346
return Err ( RepoError:: Msg( m) ) ;
347
} ;
348
crate :: credentials:: warn_once( & format! (
349
" {} : the stored token was refused; using the one an enclosing checkout has for this hub" ,
350
self .base
351
) ) ;
352
* self .token.lock ( ) .unwrap_or_else ( | p | p.into_inner ( ) ) = Some ( next) ;
353
}
354
other => return other,
355
}
356
}
357
}
358
359
fn call_once (
360
& self ,
361
post : bool ,
362
endpoint : & str ,
363
body : Option< & [ u8 ] > ,
364
token : Option< & str > ,
365
) -> Result< Vec< u8 > > {
750251b2 Andy McCall 1 month ago ↶
366
let url = format! ( " {} /oxy/v1/{endpoint} " , self .base) ;
75167672 Andy McCall 1 month ago
367
let mut req = if post {
368
self .agent.post ( & url)
369
} else {
370
self .agent.get ( & url)
371
} ;
750251b2 Andy McCall 1 month ago ↶
372
req = req.set ( " Accept" , CONTENT_TYPE ) ;
68a92484 Andy McCall 24 days ago ↶
373
if let Some ( t) = token {
750251b2 Andy McCall 1 month ago ↶
374
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
375
}
75167672 Andy McCall 1 month ago
376
if let Some ( id) = & self .repo_id {
377
req = req.set ( REPO_ID_HEADER , & hex:: encode( id) ) ;
378
}
750251b2 Andy McCall 1 month ago ↶
379
let resp = match body {
380
Some ( b) => req.set ( " Content-Type" , CONTENT_TYPE ) .send_bytes ( b) ,
381
None => req.call ( ) ,
382
} ;
383
match resp {
384
Ok ( r) => {
385
let mut out = Vec :: new( ) ;
75167672 Andy McCall 1 month ago
386
r.into_reader ( )
387
.read_to_end ( & mut out)
388
.map_err ( | e | RepoError:: Msg( format! ( " {url} : {e} " ) ) ) ? ;
750251b2 Andy McCall 1 month ago ↶
389
Ok ( out)
390
}
391
Err ( ureq:: Error:: Status( code, r) ) => {
392
let text = r.into_string ( ) .unwrap_or_default ( ) ;
3e5772cc Andy McCall 1 month ago ↶
393
let msg = format! ( " {url} : HTTP {code} : {} " , text.trim ( ) ) ;
394
Err ( RepoError:: Msg( with_hint ( msg) ) )
750251b2 Andy McCall 1 month ago ↶
395
}
3e5772cc Andy McCall 1 month ago ↶
396
397
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
750251b2 Andy McCall 1 month ago ↶
398
}
399
}
68a92484 Andy McCall 24 days ago ↶
400
}
401
402
403
404
405
fn refused ( message : & str ) -> bool {
406
[ " : HTTP 401" , " : HTTP 403" , " : HTTP 404" ]
407
.iter ( )
408
.any ( | c | message.contains ( c) )
750251b2 Andy McCall 1 month ago ↶
409
}
410
ff1f62fe Andy McCall 1 month ago ↶
411
412
413
fn refuse_plain_token ( url : & str , options : & RemoteOptions) -> Result< ( ) > {
414
if options.token.is_some ( ) & & ! crate :: credentials:: token_allowed( url) {
415
return Err ( RepoError:: Msg( format! (
416
" {url} : refusing to send a token over plain http; use https"
417
) ) ) ;
418
}
419
Ok ( ( ) )
420
}
421
3e5772cc Andy McCall 1 month ago ↶
422
423
pub fn hint_for ( message : & str ) -> Option< & 'static str > {
424
let m = message.to_ascii_lowercase ( ) ;
425
let hint = if m.contains ( " http 401" ) {
ff1f62fe Andy McCall 1 month ago ↶
426
" 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 ↶
427
} else if m.contains ( " http 403" ) {
428
" the token or account lacks permission: pushing needs a token with repo:write and write access to the repository"
429
} else if m.contains ( " http 404" ) {
ff1f62fe Andy McCall 1 month ago ↶
430
" 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 ↶
431
} else if m.contains ( " unknownissuer" )
432
| | m.contains ( " unknown issuer" )
433
| | m.contains ( " certificate" )
434
| | m.contains ( " invalidcertificate" )
435
{
436
" 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`"
437
} else if m.contains ( " connection refused" ) {
438
" nothing is listening there: check the URL and port, and that the server is running"
439
} else if m.contains ( " dns" )
440
| | m.contains ( " failed to lookup" )
441
| | m.contains ( " name or service not known" )
442
{
443
" the host name does not resolve: check the URL"
444
} else if m.contains ( " timed out" ) | | m.contains ( " timeout" ) {
445
" the server did not answer in time: check the network, or try again"
446
} else {
447
return None ;
448
} ;
449
Some ( hint)
450
}
451
452
453
pub fn with_hint ( message : String) -> String {
454
match hint_for ( & message) {
455
Some ( h) => format! ( " {message} \n hint: {h} " ) ,
456
None => message,
ff1f62fe Andy McCall 1 month ago ↶
457
}
458
}
459
460
461
462
463
pub fn http_json (
464
method : & str ,
465
url : & str ,
466
body : Option< & str > ,
467
options : & RemoteOptions,
468
) -> Result< ( u16 , String) > {
469
refuse_plain_token ( url, options) ? ;
470
let agent = ureq:: AgentBuilder:: new( )
471
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
472
.timeout ( std:: time:: Duration:: from_secs( 60 ) )
473
.build ( ) ;
474
let mut req = agent.request ( method, url) .set ( " Accept" , " application/json" ) ;
475
if let Some ( t) = & options.token {
476
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
477
}
478
let resp = match body {
479
Some ( b) => req.set ( " Content-Type" , " application/json" ) .send_string ( b) ,
480
None => req.call ( ) ,
481
} ;
482
match resp {
483
Ok ( r) => {
484
let code = r.status ( ) ;
485
Ok ( ( code, r.into_string ( ) .unwrap_or_default ( ) ) )
486
}
487
Err ( ureq:: Error:: Status( code, r) ) => Ok ( ( code, r.into_string ( ) .unwrap_or_default ( ) ) ) ,
488
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
3e5772cc Andy McCall 1 month ago ↶
489
}
490
}
491
d82e4409 Andy McCall 1 month ago ↶
492
493
494
pub fn http_get ( url : & str , options : & RemoteOptions) -> Result< Vec< u8 > > {
ff1f62fe Andy McCall 1 month ago ↶
495
refuse_plain_token ( url, options) ? ;
d82e4409 Andy McCall 1 month ago ↶
496
let agent = ureq:: AgentBuilder:: new( )
497
.tls_config ( tls_client_config ( options.ca.as_deref ( ) ) ? )
498
.build ( ) ;
499
let mut req = agent.get ( url) .set ( " Accept" , " application/json" ) ;
500
if let Some ( t) = & options.token {
501
req = req.set ( " Authorization" , & format! ( " Bearer {t} " ) ) ;
502
}
503
match req.call ( ) {
504
Ok ( r) => {
505
let mut out = Vec :: new( ) ;
506
r.into_reader ( )
507
.read_to_end ( & mut out)
508
.map_err ( | e | RepoError:: Msg( format! ( " {url} : {e} " ) ) ) ? ;
509
Ok ( out)
510
}
511
Err ( ureq:: Error:: Status( code, r) ) => {
512
let text = r.into_string ( ) .unwrap_or_default ( ) ;
3e5772cc Andy McCall 1 month ago ↶
513
Err ( RepoError:: Msg( with_hint ( format! (
4213c41d Andy McCall 1 month ago ↶
514
" {url} : HTTP {code} : {} " ,
515
text.trim ( )
3e5772cc Andy McCall 1 month ago ↶
516
) ) ) )
d82e4409 Andy McCall 1 month ago ↶
517
}
3e5772cc Andy McCall 1 month ago ↶
518
519
Err ( e) => Err ( RepoError:: Msg( with_hint ( e.to_string ( ) ) ) ) ,
d82e4409 Andy McCall 1 month ago ↶
520
}
521
}
522
750251b2 Andy McCall 1 month ago ↶
523
impl Remote for HttpRemote {
524
fn repo_id ( & self ) -> Result< [ u8 ; 16 ] > {
525
wire:: decode_capabilities( & self .call ( false , " capabilities" , None ) ? )
526
}
83e0677b Andy McCall 1 month ago ↶
527
fn refs ( & self ) -> Result< RefsResponse> {
750251b2 Andy McCall 1 month ago ↶
528
Ok ( wire:: decode_refs( & self .call ( false , " refs" , None ) ? ) ? .1 )
529
}
530
fn negotiate ( & self , have : & [ChangeHash], want : & [ChangeHash]) -> Result< Vec< ChangeHash> > {
75167672 Andy McCall 1 month ago
531
wire:: decode_hashes( & self .call (
532
true ,
533
" negotiate" ,
534
Some ( & wire:: encode_negotiate( have, want) ) ,
535
) ? )
750251b2 Andy McCall 1 month ago ↶
536
}
b3c5a59a Andy McCall 1 month ago ↶
537
fn fetch ( & self , changes : & [ChangeHash], max_blob : Option< u64 > ) -> Result< Bundle> {
75167672 Andy McCall 1 month ago
538
wire:: decode_bundle( & self .call (
539
true ,
540
" fetch" ,
541
Some ( & wire:: encode_fetch( changes, max_blob) ) ,
542
) ? )
750251b2 Andy McCall 1 month ago ↶
543
}
b3c5a59a Andy McCall 1 month ago ↶
544
fn objects ( & self , refs : & [ObjectRef]) -> Result< Vec< ( ObjectType, Vec< u8 > ) > > {
75167672 Andy McCall 1 month ago
545
Ok (
546
wire:: decode_bundle( & self .call ( true , " objects" , Some ( & wire:: encode_hashes( refs) ) ) ? ) ?
547
.objects,
548
)
b3c5a59a Andy McCall 1 month ago ↶
549
}
2863456a Andy McCall 1 month ago ↶
550
fn checkpoint ( & self , heads : & [ChangeHash]) -> Result< Bundle> {
551
wire:: decode_bundle( & self .call ( true , " checkpoint" , Some ( & wire:: encode_hashes( heads) ) ) ? )
552
}
553
fn shallow ( & self , heads : & [ChangeHash], depth : u32 ) -> Result< Vec< ChangeHash> > {
75167672 Andy McCall 1 month ago
554
wire:: decode_hashes( & self .call (
555
true ,
556
" shallow" ,
557
Some ( & wire:: encode_shallow( heads, depth) ) ,
558
) ? )
2863456a Andy McCall 1 month ago ↶
559
}
75167672 Andy McCall 1 month ago
560
fn push (
561
& self ,
562
bundle : Bundle,
563
updates : Vec< RefUpdate> ,
564
policy : Option< SignedPolicy> ,
565
tags : Vec< TagRef> ,
566
) -> Result< Vec< RefResult> > {
567
wire:: decode_push_response( & self .call (
568
true ,
569
" push" ,
570
Some ( & wire:: encode_push(
571
& bundle,
572
& updates,
573
policy.as_ref ( ) ,
574
& tags,
575
) ) ,
576
) ? )
750251b2 Andy McCall 1 month ago ↶
577
}
578
}
579
827281ee Andy McCall 1 month ago ↶
580
581
582
583
pub fn stored_url ( url : & str ) -> String {
584
if url.starts_with ( " http://" ) | | url.starts_with ( " https://" ) {
585
return url.to_string ( ) ;
586
}
9f9e72b9 Andy McCall 1 month ago ↶
587
let ( prefix, path) = match ( url.strip_prefix ( " file://" ) , url.strip_prefix ( " dir://" ) ) {
588
( Some ( p) , _ ) => ( " file://" , p) ,
589
( _ , Some ( p) ) => ( " dir://" , p) ,
590
_ => ( " " , url) ,
827281ee Andy McCall 1 month ago ↶
591
} ;
592
match std:: path:: absolute( path) {
593
Ok ( abs) => format! ( " {prefix} {} " , abs.display ( ) ) ,
594
Err ( _ ) => url.to_string ( ) ,
595
}
596
}
597
9f9e72b9 Andy McCall 1 month ago ↶
598
599
2b5ee371 Andy McCall 1 month ago ↶
600
pub fn open_remote ( url : & str , options : & RemoteOptions) -> Result< Box< dyn Remote> > {
750251b2 Andy McCall 1 month ago ↶
601
if url.starts_with ( " http://" ) | | url.starts_with ( " https://" ) {
2b5ee371 Andy McCall 1 month ago ↶
602
return Ok ( Box :: new( HttpRemote:: new( url, options) ? ) ) ;
9f9e72b9 Andy McCall 1 month ago ↶
603
}
604
if let Some ( store) = open_store ( url, options) {
605
return crate :: repo:: strata:: open_store_remote( store? , options) ;
750251b2 Andy McCall 1 month ago ↶
606
}
5b9c6ad4 Andy McCall 1 month ago ↶
607
let path = url.strip_prefix ( " file://" ) .unwrap_or ( url) ;
608
if path.contains ( " ://" ) {
750251b2 Andy McCall 1 month ago ↶
609
return Err ( RepoError:: Msg( format! ( " unsupported remote URL {url:?} " ) ) ) ;
5b9c6ad4 Andy McCall 1 month ago ↶
610
}
84b9e9ac Andy McCall 1 month ago ↶
611
let mut local = LocalRemote:: open( Path:: new( path) ) ? ;
612
local.claimed_id = options.repo_id;
613
Ok ( Box :: new( local) )
5b9c6ad4 Andy McCall 1 month ago ↶
614
}
750251b2 Andy McCall 1 month ago ↶
615
616
617
618
619
pub mod wire {
620
use super :: * ;
621
622
fn schema ( msg : & str ) -> RepoError {
623
RepoError:: Object( ObjectError:: Schema( msg.into ( ) ) )
624
}
625
fn sigs_value ( sigs : & [Signature]) -> Value {
626
Value:: Array( sigs.iter ( ) .map ( | s | s.to_value ( ) ) .collect ( ) )
627
}
628
fn sigs_from ( v : & Value) -> Result< Vec< Signature> > {
75167672 Andy McCall 1 month ago
629
Ok ( o:: array( v, " signatures" ) ?
630
.iter ( )
631
.map ( Signature:: from_value)
632
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( ) ? )
750251b2 Andy McCall 1 month ago ↶
633
}
634
fn hashes_value ( hs : & [ObjectRef]) -> Value {
635
Value:: Array( hs.iter ( ) .map ( | h | Value:: Bytes( h.0. to_vec ( ) ) ) .collect ( ) )
636
}
637
fn hashes_from ( v : & Value) -> Result< Vec< ObjectRef> > {
75167672 Andy McCall 1 month ago
638
Ok ( o:: array( v, " hashes" ) ?
639
.iter ( )
640
.map ( | h | o:: oref( h, " hash" ) )
641
.collect:: < std:: result:: Result< Vec< _ > , _ > > ( ) ? )
750251b2 Andy McCall 1 month ago ↶
642
}
643
fn attest_value ( sa : & SignedAttest) -> Value {
644
Value:: Array( vec! [ Value:: Bytes( sa.attest.encode ( ) ) , sigs_value ( & sa.sigs) ] )
645
}
646
fn attest_from ( v : & Value) -> Result< SignedAttest> {
647
let a = o:: array( v, " signed attest" ) ? ;
648
if a.len ( ) ! = 2 {
649
return Err ( schema ( " signed attest: expected [attest, sigs]" ) ) ;
650
}
75167672 Andy McCall 1 month ago
651
Ok ( SignedAttest {
652
attest: Attest:: decode( o:: bytes( & a[ 0 ] , " attest" ) ? ) ? ,
653
sigs: sigs_from ( & a[ 1 ] ) ? ,
654
} )
750251b2 Andy McCall 1 month ago ↶
655
}
656
fn opt_attest_value ( sa : & Option< SignedAttest> ) -> Value {
657
sa.as_ref ( ) .map ( attest_value) .unwrap_or ( Value:: Null)
658
}
659
fn opt_attest_from ( v : & Value) -> Result< Option< SignedAttest> > {
75167672 Andy McCall 1 month ago
660
if v.is_null ( ) {
661
Ok ( None )
662
} else {
663
attest_from ( v) .map ( Some )
664
}
750251b2 Andy McCall 1 month ago ↶
665
}
28fc23d7 Andy McCall 1 month ago ↶
666
667
668
pub fn policy_value ( sp : & SignedPolicy) -> Value {
669
Value:: Array( vec! [ Value:: Bytes( sp.policy.encode ( ) ) , sigs_value ( & sp.sigs) ] )
83e0677b Andy McCall 1 month ago ↶
670
}
28fc23d7 Andy McCall 1 month ago ↶
671
pub fn policy_from ( v : & Value) -> Result< SignedPolicy> {
83e0677b Andy McCall 1 month ago ↶
672
let a = o:: array( v, " signed policy" ) ? ;
673
if a.len ( ) ! = 2 {
674
return Err ( schema ( " signed policy: expected [policy, sigs]" ) ) ;
675
}
75167672 Andy McCall 1 month ago
676
Ok ( SignedPolicy {
677
policy: Policy:: decode( o:: bytes( & a[ 0 ] , " policy" ) ? ) ? ,
678
sigs: sigs_from ( & a[ 1 ] ) ? ,
679
} )
28fc23d7 Andy McCall 1 month ago ↶
680
}
681
fn opt_policy_value ( sp : Option< & SignedPolicy> ) -> Value {
682
sp.map ( policy_value) .unwrap_or ( Value:: Null)
683
}
684
fn opt_policy_from ( v : & Value) -> Result< Option< SignedPolicy> > {
75167672 Andy McCall 1 month ago
685
if v.is_null ( ) {
686
Ok ( None )
687
} else {
688
policy_from ( v) .map ( Some )
689
}
83e0677b Andy McCall 1 month ago ↶
690
}
750251b2 Andy McCall 1 month ago ↶
691
fn map < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a o:: Map> {
692
Ok ( o:: map( v, what) ? )
693
}
694
fn field < 'a > ( m : & 'a o:: Map, k : u64 , what : & str ) -> Result< & 'a Value> {
695
Ok ( o:: field( m, k, what) ? )
696
}
697
6f8aeb99 Andy McCall 1 month ago ↶
698
699
700
750251b2 Andy McCall 1 month ago ↶
701
pub fn bundle_value ( b : & Bundle) -> Value {
2c75672f Andy McCall 1 month ago ↶
702
let mut changes = Vec :: with_capacity( b.raw_changes.len ( ) + b.changes.len ( ) ) ;
703
704
705
for ( bytes, s) in & b.raw_changes {
706
changes.push ( Value:: Array( vec! [
707
Value:: Bytes( bytes.clone ( ) ) ,
708
sigs_value ( s) ,
709
] ) ) ;
710
}
6f8aeb99 Andy McCall 1 month ago ↶
711
let mut texts: Vec< ( ObjectType, Vec< u8 > ) > = Vec :: new( ) ;
712
let mut listed: Option< std:: collections:: HashSet< ObjectRef> > = None ;
713
for ( c, s) in & b.changes {
714
let ( bytes, stored) = c.encode_parts ( ) ;
715
for t in stored {
716
let listed = listed.get_or_insert_with ( | | {
717
b.objects
718
.iter ( )
719
.map ( | ( ty , body ) | crate :: hash:: hash_object( * ty, body) )
720
.collect ( )
721
} ) ;
722
if ! listed.insert ( t.blob.hash ( ) ) {
723
continue ;
724
}
725
for chunk in crate :: chunk:: chunks( & t.content) {
726
if listed.insert ( crate :: hash:: hash_object( ObjectType:: Chunk, chunk) ) {
727
texts.push ( ( ObjectType:: Chunk, chunk.to_vec ( ) ) ) ;
728
}
729
}
730
texts.push ( ( ObjectType:: Blob, t.blob.encode ( ) ) ) ;
731
}
732
changes.push ( Value:: Array( vec! [ Value:: Bytes( bytes) , sigs_value ( s) ] ) ) ;
733
}
750251b2 Andy McCall 1 month ago ↶
734
Value:: map( [
6f8aeb99 Andy McCall 1 month ago ↶
735
( 0 , Value:: Array( changes) ) ,
750251b2 Andy McCall 1 month ago ↶
736
(
75167672 Andy McCall 1 month ago
737
1 ,
738
Value:: Array( b.attests.iter ( ) .map ( attest_value) .collect ( ) ) ,
739
) ,
740
(
750251b2 Andy McCall 1 month ago ↶
741
2 ,
742
Value:: Array(
75167672 Andy McCall 1 month ago
743
b.objects
744
.iter ( )
6f8aeb99 Andy McCall 1 month ago ↶
745
.chain ( & texts)
75167672 Andy McCall 1 month ago
746
.map ( | ( t , body ) | {
747
Value:: Array( vec! [
748
Value:: Text( t.name ( ) .into ( ) ) ,
749
Value:: Bytes( body.clone ( ) ) ,
750
] )
751
} )
752
.collect ( ) ,
750251b2 Andy McCall 1 month ago ↶
753
) ,
754
) ,
75167672 Andy McCall 1 month ago
755
(
756
3 ,
757
Value:: Array( b.policies.iter ( ) .map ( policy_value) .collect ( ) ) ,
758
) ,
750251b2 Andy McCall 1 month ago ↶
759
] )
760
}
761
762
pub fn bundle_from_value ( v : & Value) -> Result< Bundle> {
763
let m = map ( v, " bundle" ) ? ;
764
let mut b = Bundle:: default( ) ;
765
for ob in o:: array( field ( m, 2 , " objects" ) ? , " objects" ) ? {
766
let pair = o:: array( ob, " object" ) ? ;
767
if pair.len ( ) ! = 2 {
768
return Err ( schema ( " object: expected [type, body]" ) ) ;
769
}
75167672 Andy McCall 1 month ago
770
let ty = ObjectType:: from_name( o:: text( & pair[ 0 ] , " object type" ) ? )
771
.ok_or_else ( | | schema ( " unknown object type" ) ) ? ;
772
b.objects
773
.push ( ( ty, o:: bytes( & pair[ 1 ] , " object body" ) ? .to_vec ( ) ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
774
}
775
776
let mut by_ref: Option< std:: collections:: HashMap< ObjectRef, usize > > = None ;
777
for c in o:: array( field ( m, 0 , " changes" ) ? , " changes" ) ? {
778
let pair = o:: array( c, " change entry" ) ? ;
779
if pair.len ( ) ! = 2 {
780
return Err ( schema ( " change entry: expected [change, sigs]" ) ) ;
781
}
598009a7 Andy McCall 1 month ago ↶
782
let encoded = o:: bytes( & pair[ 0 ] , " change" ) ? ;
783
b.hashes
784
.push ( crate :: hash:: hash_object( ObjectType:: Change, encoded) ) ;
0f93f49c Andy McCall 1 month ago ↶
785
let change = Change:: decode_with( encoded, & mut | text, count, form| {
6f8aeb99 Andy McCall 1 month ago ↶
786
let by_ref = by_ref.get_or_insert_with ( | | {
787
b.objects
788
.iter ( )
789
.enumerate ( )
790
.map ( | ( i , (ty , body ) ) | ( crate :: hash:: hash_object( * ty, body) , i) )
791
.collect ( )
792
} ) ;
793
let get =
794
| r : & ObjectRef, want : ObjectType| match by_ref.get ( r) .map ( | i | & b.objects[ * i] ) {
795
Some ( ( ty, body) ) if * ty = = want => Ok ( body) ,
796
_ => Err ( ObjectError:: Schema( format! (
797
" the bundle lacks {} {} " ,
798
want.name ( ) ,
799
r.short ( 12 )
800
) ) ) ,
801
} ;
802
let blob = crate :: objects:: Blob:: decode( get ( text, ObjectType:: Blob) ? ) ? ;
803
let mut content = Vec :: with_capacity( blob.length as usize ) ;
804
for chunk in & blob.chunks {
805
content.extend_from_slice ( get ( chunk, ObjectType:: Chunk) ? ) ;
806
}
0f93f49c Andy McCall 1 month ago ↶
807
crate :: objects:: text_lines( & content, count, form)
6f8aeb99 Andy McCall 1 month ago ↶
808
} ) ? ;
809
b.changes.push ( ( change, sigs_from ( & pair[ 1 ] ) ? ) ) ;
810
}
811
for a in o:: array( field ( m, 1 , " attests" ) ? , " attests" ) ? {
812
b.attests.push ( attest_from ( a) ? ) ;
750251b2 Andy McCall 1 month ago ↶
813
}
28fc23d7 Andy McCall 1 month ago ↶
814
if let Some ( ps) = m.get ( & 3 ) {
815
for p in o:: array( ps, " policies" ) ? {
816
b.policies.push ( policy_from ( p) ? ) ;
817
}
818
}
750251b2 Andy McCall 1 month ago ↶
819
Ok ( b)
820
}
821
822
pub fn encode_bundle ( b : & Bundle) -> Vec< u8 > {
823
cbor:: encode( & bundle_value ( b) )
824
}
825
pub fn decode_bundle ( bytes : & [u8 ]) -> Result< Bundle> {
826
bundle_from_value ( & cbor:: decode( bytes) .map_err ( ObjectError:: from) ? )
827
}
828
829
pub fn encode_capabilities ( id : [u8 ; 16]) -> Vec< u8 > {
830
cbor:: encode( & Value:: map( [
831
( 0 , Value:: Array( vec! [ Value:: Uint( PROTOCOL_VERSION ) ] ) ) ,
832
( 1 , Value:: Bytes( id.to_vec ( ) ) ) ,
833
( 2 , Value:: Array( vec! [ Value:: Text( " seal" .into ( ) ) ] ) ) ,
834
] ) )
835
}
836
pub fn decode_capabilities ( bytes : & [u8 ]) -> Result< [ u8 ; 16 ] > {
837
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
838
let m = map ( & v, " capabilities" ) ? ;
839
let versions = o:: array( field ( m, 0 , " versions" ) ? , " versions" ) ? ;
75167672 Andy McCall 1 month ago
840
if ! versions
841
.iter ( )
842
.any ( | x | x.as_uint ( ) = = Some ( PROTOCOL_VERSION ) )
843
{
844
return Err ( RepoError:: Msg(
845
" remote does not speak protocol version 1" .into ( ) ,
846
) ) ;
750251b2 Andy McCall 1 month ago ↶
847
}
75167672 Andy McCall 1 month ago
848
o:: bytes( field ( m, 1 , " repo id" ) ? , " repo id" ) ?
849
.try_into ( )
850
.map_err ( | _| schema ( " repo id: expected 16 bytes" ) )
750251b2 Andy McCall 1 month ago ↶
851
}
852
132b884b Andy McCall 1 month ago ↶
853
fn tags_value ( tags : & [TagRef]) -> Value {
854
Value:: Array(
855
tags.iter ( )
856
.map ( | t | {
857
Value:: map( [
858
( 0 , Value:: Text( t.name.clone ( ) ) ) ,
859
( 1 , Value:: Bytes( t.state.0. to_vec ( ) ) ) ,
860
( 2 , hashes_value ( & t.heads) ) ,
861
( 3 , opt_attest_value ( & t.attestation) ) ,
862
] )
863
} )
864
.collect ( ) ,
865
)
866
}
867
fn tags_from ( v : & Value) -> Result< Vec< TagRef> > {
868
let mut out = Vec :: new( ) ;
869
for t in o:: array( v, " tags" ) ? {
870
let tm = map ( t, " tag" ) ? ;
871
out.push ( TagRef {
872
name: o:: text( field ( tm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
873
state: o:: oref( field ( tm, 1 , " state" ) ? , " state" ) ? ,
874
heads: hashes_from ( field ( tm, 2 , " heads" ) ? ) ? ,
875
attestation: opt_attest_from ( field ( tm, 3 , " attestation" ) ? ) ? ,
876
} ) ;
877
}
878
Ok ( out)
879
}
880
83e0677b Andy McCall 1 month ago ↶
881
pub fn encode_refs ( id : [u8 ; 16], resp : & RefsResponse) -> Vec< u8 > {
750251b2 Andy McCall 1 month ago ↶
882
cbor:: encode( & Value:: map( [
883
(
884
0 ,
885
Value:: Array(
83e0677b Andy McCall 1 month ago ↶
886
resp.refs
887
.iter ( )
750251b2 Andy McCall 1 month ago ↶
888
.map ( | r | {
889
Value:: map( [
890
( 0 , Value:: Text( r.name.clone ( ) ) ) ,
891
( 1 , Value:: Bytes( r.state.0. to_vec ( ) ) ) ,
892
( 2 , hashes_value ( & r.heads) ) ,
893
( 3 , opt_attest_value ( & r.attestation) ) ,
894
( 4 , r.last_serial.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
895
] )
896
} )
897
.collect ( ) ,
898
) ,
899
) ,
900
( 1 , Value:: Bytes( id.to_vec ( ) ) ) ,
83e0677b Andy McCall 1 month ago ↶
901
( 2 , opt_policy_value ( resp.policy.as_ref ( ) ) ) ,
132b884b Andy McCall 1 month ago ↶
902
( 3 , tags_value ( & resp.tags) ) ,
75167672 Andy McCall 1 month ago
903
(
904
4 ,
905
Value:: Array( resp.history.iter ( ) .map ( policy_value) .collect ( ) ) ,
906
) ,
14edbaaf Andy McCall 1 month ago ↶
907
(
908
5 ,
909
resp.default_bookmark
910
.clone ( )
911
.map ( Value:: Text)
912
.unwrap_or ( Value:: Null) ,
913
) ,
750251b2 Andy McCall 1 month ago ↶
914
] ) )
915
}
83e0677b Andy McCall 1 month ago ↶
916
pub fn decode_refs ( bytes : & [u8 ]) -> Result< ( [u8 ; 16], RefsResponse) > {
750251b2 Andy McCall 1 month ago ↶
917
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
918
let m = map ( & v, " refs" ) ? ;
75167672 Andy McCall 1 month ago
919
let id: [ u8 ; 16 ] = o:: bytes( field ( m, 1 , " repo id" ) ? , " repo id" ) ?
920
.try_into ( )
921
.map_err ( | _| schema ( " repo id: expected 16 bytes" ) ) ? ;
750251b2 Andy McCall 1 month ago ↶
922
let mut refs = Vec :: new( ) ;
923
for r in o:: array( field ( m, 0 , " refs" ) ? , " refs" ) ? {
924
let rm = map ( r, " ref" ) ? ;
925
let ls = field ( rm, 4 , " last serial" ) ? ;
926
refs.push ( RemoteRef {
927
name: o:: text( field ( rm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
928
state: o:: oref( field ( rm, 1 , " state" ) ? , " state" ) ? ,
929
heads: hashes_from ( field ( rm, 2 , " heads" ) ? ) ? ,
930
attestation: opt_attest_from ( field ( rm, 3 , " attestation" ) ? ) ? ,
75167672 Andy McCall 1 month ago
931
last_serial: if ls.is_null ( ) {
932
None
933
} else {
934
Some ( o:: uint( ls, " last serial" ) ? )
935
} ,
750251b2 Andy McCall 1 month ago ↶
936
} ) ;
937
}
83e0677b Andy McCall 1 month ago ↶
938
let policy = match m.get ( & 2 ) {
939
Some ( p) => opt_policy_from ( p) ? ,
940
None => None ,
941
} ;
132b884b Andy McCall 1 month ago ↶
942
let tags = match m.get ( & 3 ) {
943
Some ( t) => tags_from ( t) ? ,
944
None => Vec :: new( ) ,
945
} ;
28fc23d7 Andy McCall 1 month ago ↶
946
let mut history = Vec :: new( ) ;
947
if let Some ( h) = m.get ( & 4 ) {
948
for p in o:: array( h, " history" ) ? {
949
history.push ( policy_from ( p) ? ) ;
950
}
951
}
14edbaaf Andy McCall 1 month ago ↶
952
let default_bookmark = match m.get ( & 5 ) {
953
Some ( Value:: Text( t) ) => Some ( t.clone ( ) ) ,
954
_ => None ,
955
} ;
75167672 Andy McCall 1 month ago
956
Ok ( (
957
id,
958
RefsResponse {
959
refs,
960
policy,
961
tags,
962
history,
14edbaaf Andy McCall 1 month ago ↶
963
default_bookmark,
75167672 Andy McCall 1 month ago
964
} ,
965
) )
750251b2 Andy McCall 1 month ago ↶
966
}
967
968
pub fn encode_negotiate ( have : & [ChangeHash], want : & [ChangeHash]) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
969
cbor:: encode( & Value:: map( [
970
( 0 , hashes_value ( have) ) ,
971
( 1 , hashes_value ( want) ) ,
972
] ) )
750251b2 Andy McCall 1 month ago ↶
973
}
974
pub fn decode_negotiate ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , Vec< ChangeHash> ) > {
975
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
976
let m = map ( & v, " negotiate" ) ? ;
75167672 Andy McCall 1 month ago
977
Ok ( (
978
hashes_from ( field ( m, 0 , " have" ) ? ) ? ,
979
hashes_from ( field ( m, 1 , " want" ) ? ) ? ,
980
) )
750251b2 Andy McCall 1 month ago ↶
981
}
982
b3c5a59a Andy McCall 1 month ago ↶
983
984
pub fn encode_fetch ( hs : & [ChangeHash], max_blob : Option< u64 > ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
985
cbor:: encode( & Value:: map( [
986
( 0 , hashes_value ( hs) ) ,
987
( 1 , max_blob.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
988
] ) )
b3c5a59a Andy McCall 1 month ago ↶
989
}
990
pub fn decode_fetch ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , Option< u64 > ) > {
991
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
992
let m = map ( & v, " fetch" ) ? ;
993
let max = match m.get ( & 1 ) {
994
Some ( x) if ! x.is_null ( ) => Some ( o:: uint( x, " max blob" ) ? ) ,
995
_ => None ,
996
} ;
997
Ok ( ( hashes_from ( field ( m, 0 , " changes" ) ? ) ? , max) )
998
}
999
2863456a Andy McCall 1 month ago ↶
1000
1001
pub fn encode_shallow ( hs : & [ChangeHash], depth : u32 ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
1002
cbor:: encode( & Value:: map( [
1003
( 0 , hashes_value ( hs) ) ,
1004
( 1 , Value:: Uint( depth as u64 ) ) ,
1005
] ) )
2863456a Andy McCall 1 month ago ↶
1006
}
1007
pub fn decode_shallow ( bytes : & [u8 ]) -> Result< ( Vec< ChangeHash> , u32 ) > {
1008
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1009
let m = map ( & v, " shallow" ) ? ;
75167672 Andy McCall 1 month ago
1010
Ok ( (
1011
hashes_from ( field ( m, 0 , " heads" ) ? ) ? ,
1012
o:: u32_( field ( m, 1 , " depth" ) ? , " depth" ) ? ,
1013
) )
2863456a Andy McCall 1 month ago ↶
1014
}
1015
750251b2 Andy McCall 1 month ago ↶
1016
pub fn encode_hashes ( hs : & [ChangeHash]) -> Vec< u8 > {
1017
cbor:: encode( & Value:: map( [ ( 0 , hashes_value ( hs) ) ] ) )
1018
}
1019
pub fn decode_hashes ( bytes : & [u8 ]) -> Result< Vec< ChangeHash> > {
1020
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1021
hashes_from ( field ( map ( & v, " hashes" ) ? , 0 , " hashes" ) ? )
1022
}
1023
75167672 Andy McCall 1 month ago
1024
pub fn encode_push (
1025
bundle : & Bundle,
1026
updates : & [RefUpdate],
1027
policy : Option< & SignedPolicy> ,
1028
tags : & [TagRef],
1029
) -> Vec< u8 > {
750251b2 Andy McCall 1 month ago ↶
1030
cbor:: encode( & Value:: map( [
1031
( 0 , bundle_value ( bundle) ) ,
83e0677b Andy McCall 1 month ago ↶
1032
( 2 , opt_policy_value ( policy) ) ,
132b884b Andy McCall 1 month ago ↶
1033
( 3 , tags_value ( tags) ) ,
750251b2 Andy McCall 1 month ago ↶
1034
(
1035
1 ,
1036
Value:: Array(
1037
updates
1038
.iter ( )
1039
.map ( | u | {
1040
Value:: map( [
1041
( 0 , Value:: Text( u.name.clone ( ) ) ) ,
1042
( 1 , Value:: Bytes( u.old_state.0. to_vec ( ) ) ) ,
1043
( 2 , Value:: Bytes( u.new_state.0. to_vec ( ) ) ) ,
1044
( 3 , Value:: Bool( u.force) ) ,
1045
( 4 , opt_attest_value ( & u.attestation) ) ,
1046
] )
1047
} )
1048
.collect ( ) ,
1049
) ,
1050
) ,
1051
] ) )
1052
}
132b884b Andy McCall 1 month ago ↶
1053
1054
pub type PushParts = ( Bundle, Vec< RefUpdate> , Option< SignedPolicy> , Vec< TagRef> ) ;
1055
1056
pub fn decode_push ( bytes : & [u8 ]) -> Result< PushParts> {
750251b2 Andy McCall 1 month ago ↶
1057
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1058
let m = map ( & v, " push" ) ? ;
1059
let bundle = bundle_from_value ( field ( m, 0 , " bundle" ) ? ) ? ;
1060
let mut updates = Vec :: new( ) ;
1061
for u in o:: array( field ( m, 1 , " updates" ) ? , " updates" ) ? {
1062
let um = map ( u, " update" ) ? ;
1063
updates.push ( RefUpdate {
1064
name: o:: text( field ( um, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
1065
old_state: o:: oref( field ( um, 1 , " old_state" ) ? , " old_state" ) ? ,
1066
new_state: o:: oref( field ( um, 2 , " new_state" ) ? , " new_state" ) ? ,
75167672 Andy McCall 1 month ago
1067
force: field ( um, 3 , " force" ) ?
1068
.as_bool ( )
1069
.ok_or_else ( | | schema ( " force: expected bool" ) ) ? ,
750251b2 Andy McCall 1 month ago ↶
1070
attestation: opt_attest_from ( field ( um, 4 , " attestation" ) ? ) ? ,
1071
} ) ;
1072
}
83e0677b Andy McCall 1 month ago ↶
1073
let policy = match m.get ( & 2 ) {
1074
Some ( p) => opt_policy_from ( p) ? ,
1075
None => None ,
1076
} ;
132b884b Andy McCall 1 month ago ↶
1077
let tags = match m.get ( & 3 ) {
1078
Some ( t) => tags_from ( t) ? ,
1079
None => Vec :: new( ) ,
1080
} ;
1081
Ok ( ( bundle, updates, policy, tags) )
750251b2 Andy McCall 1 month ago ↶
1082
}
1083
1084
pub fn encode_push_response ( results : & [RefResult]) -> Vec< u8 > {
1085
cbor:: encode( & Value:: map( [ (
1086
0 ,
1087
Value:: Array(
1088
results
1089
.iter ( )
75167672 Andy McCall 1 month ago
1090
.map ( | r | {
1091
Value:: map( [
1092
( 0 , Value:: Text( r.name.clone ( ) ) ) ,
1093
( 1 , Value:: Bool( r.ok) ) ,
1094
( 2 , Value:: Text( r.reason.clone ( ) ) ) ,
1095
] )
1096
} )
750251b2 Andy McCall 1 month ago ↶
1097
.collect ( ) ,
1098
) ,
1099
) ] ) )
1100
}
1101
pub fn decode_push_response ( bytes : & [u8 ]) -> Result< Vec< RefResult> > {
1102
let v = cbor:: decode( bytes) .map_err ( ObjectError:: from) ? ;
1103
let m = map ( & v, " push response" ) ? ;
1104
let mut out = Vec :: new( ) ;
1105
for r in o:: array( field ( m, 0 , " results" ) ? , " results" ) ? {
1106
let rm = map ( r, " result" ) ? ;
1107
out.push ( RefResult {
1108
name: o:: text( field ( rm, 0 , " name" ) ? , " name" ) ? .to_owned ( ) ,
75167672 Andy McCall 1 month ago
1109
ok: field ( rm, 1 , " ok" ) ?
1110
.as_bool ( )
1111
.ok_or_else ( | | schema ( " ok: expected bool" ) ) ? ,
750251b2 Andy McCall 1 month ago ↶
1112
reason: o:: text( field ( rm, 2 , " reason" ) ? , " reason" ) ? .to_owned ( ) ,
1113
} ) ;
1114
}
1115
Ok ( out)
1116
}
1117
}
1118
1119
# [ cfg ( test ) ]
1120
mod tests {
1121
use super :: * ;
1122
use crate :: attest:: AttestBody;
1123
use crate :: objects:: Hlc;
1124
1125
# [ test ]
1126
fn wire_round_trips ( ) {
1127
let c = crate :: objects:: tests:: sample_change( ) ;
75167672 Andy McCall 1 month ago
1128
let sk = crate :: identity:: SecretKey {
1129
ed_seed: [ 1 ; 32 ] ,
1130
ml_seed: [ 2 ; 32 ] ,
1131
} ;
750251b2 Andy McCall 1 month ago ↶
1132
let id = sk.identity ( " a" , " b" , 0 ) ;
1133
let key = id.hash ( ) ;
1134
let sig = sk.sign ( crate :: identity:: Domain:: Change, & key, & c.hash ( ) ) ;
1135
let seal = Attest:: seal( [ 3 ; 16 ] , vec! [ c.hash ( ) ] ) ;
1136
let seal_sig = sk.sign ( crate :: identity:: Domain:: Attest, & key, & seal.hash ( ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
1137
let policy = Policy:: single_root( [ 3 ; 16 ] , id.clone ( ) , 99 ) ;
1138
let psig = sk.sign ( crate :: identity:: Domain:: Policy, & key, & policy.hash ( ) ) ;
750251b2 Andy McCall 1 month ago ↶
1139
let bundle = Bundle {
1140
changes: vec! [ ( c.clone ( ) , vec! [ sig.clone ( ) ] ) ] ,
75167672 Andy McCall 1 month ago
1141
attests: vec! [ SignedAttest {
1142
attest: seal.clone ( ) ,
1143
sigs: vec! [ seal_sig] ,
1144
} ] ,
1145
objects: vec! [
1146
( ObjectType:: Identity, id.encode ( ) ) ,
1147
( ObjectType:: Chunk, b " xyz" .to_vec ( ) ) ,
1148
] ,
1149
policies: vec! [ SignedPolicy {
1150
policy: policy.clone ( ) ,
1151
sigs: vec! [ psig.clone ( ) ] ,
1152
} ] ,
598009a7 Andy McCall 1 month ago ↶
1153
hashes: vec! [ ] ,
2c75672f Andy McCall 1 month ago ↶
1154
raw_changes: vec! [ ] ,
750251b2 Andy McCall 1 month ago ↶
1155
} ;
1156
let d = wire:: decode_bundle( & wire:: encode_bundle( & bundle) ) .unwrap ( ) ;
598009a7 Andy McCall 1 month ago ↶
1157
assert_eq! (
1158
d.hashes,
1159
vec! [ c.hash ( ) ] ,
1160
" the receiver hashes what it was sent"
1161
) ;
750251b2 Andy McCall 1 month ago ↶
1162
assert_eq! ( d.changes, bundle.changes) ;
1163
assert_eq! ( d.attests, bundle.attests) ;
1164
assert_eq! ( d.objects, bundle.objects) ;
28fc23d7 Andy McCall 1 month ago ↶
1165
assert_eq! ( d.policies, bundle.policies) ;
750251b2 Andy McCall 1 month ago ↶
1166
75167672 Andy McCall 1 month ago
1167
let att = Attest {
1168
repo: [ 3 ; 16 ] ,
1169
hlc: Hlc:: default( ) ,
1170
body: AttestBody:: Ref {
1171
name: " main" .into ( ) ,
1172
old_state: ObjectRef( [ 1 ; 32 ] ) ,
1173
new_state: ObjectRef( [ 2 ; 32 ] ) ,
1174
serial: 4 ,
1175
} ,
1176
} ;
750251b2 Andy McCall 1 month ago ↶
1177
let refs = vec! [
75167672 Andy McCall 1 month ago
1178
RemoteRef {
1179
name: " main" .into ( ) ,
1180
state: ObjectRef( [ 2 ; 32 ] ) ,
1181
heads: vec! [ c.hash ( ) ] ,
1182
attestation: Some ( SignedAttest {
1183
attest: att.clone ( ) ,
1184
sigs: vec! [ sig.clone ( ) ] ,
1185
} ) ,
1186
last_serial: Some ( 4 ) ,
1187
} ,
1188
RemoteRef {
1189
name: " dev" .into ( ) ,
1190
state: ObjectRef( [ 5 ; 32 ] ) ,
1191
heads: vec! [ ] ,
1192
attestation: None ,
1193
last_serial: None ,
1194
} ,
750251b2 Andy McCall 1 month ago ↶
1195
] ;
75167672 Andy McCall 1 month ago
1196
let tag = TagRef {
1197
name: " v1" .into ( ) ,
1198
state: ObjectRef( [ 2 ; 32 ] ) ,
1199
heads: vec! [ c.hash ( ) ] ,
1200
attestation: None ,
1201
} ;
1202
let resp = RefsResponse {
1203
refs,
1204
policy: Some ( SignedPolicy {
1205
policy: policy.clone ( ) ,
1206
sigs: vec! [ psig.clone ( ) ] ,
1207
} ) ,
1208
tags: vec! [ tag.clone ( ) ] ,
1209
history: vec! [ SignedPolicy {
1210
policy: policy.clone ( ) ,
1211
sigs: vec! [ ] ,
1212
} ] ,
14edbaaf Andy McCall 1 month ago ↶
1213
default_bookmark: Some ( " develop" .into ( ) ) ,
75167672 Andy McCall 1 month ago
1214
} ;
83e0677b Andy McCall 1 month ago ↶
1215
let ( id_back, back) = wire:: decode_refs( & wire:: encode_refs( [ 3 ; 16 ] , & resp) ) .unwrap ( ) ;
750251b2 Andy McCall 1 month ago ↶
1216
assert_eq! ( id_back, [ 3 ; 16 ] ) ;
83e0677b Andy McCall 1 month ago ↶
1217
assert_eq! ( back.refs.len ( ) , 2 ) ;
1218
assert_eq! ( back.refs[ 0 ] .attestation.as_ref ( ) .unwrap ( ) .attest, att) ;
1219
assert_eq! ( back.refs[ 1 ] .last_serial, None ) ;
1220
assert_eq! ( back.policy.as_ref ( ) .unwrap ( ) .policy, policy) ;
28fc23d7 Andy McCall 1 month ago ↶
1221
assert_eq! ( back.history, resp.history) ;
132b884b Andy McCall 1 month ago ↶
1222
assert_eq! ( back.tags, vec! [ tag.clone ( ) ] ) ;
750251b2 Andy McCall 1 month ago ↶
1223
75167672 Andy McCall 1 month ago
1224
let ( h, w) = wire:: decode_negotiate( & wire:: encode_negotiate(
1225
& [ ObjectRef( [ 1 ; 32 ] ) ] ,
1226
& [ ObjectRef( [ 2 ; 32 ] ) ] ,
1227
) )
1228
.unwrap ( ) ;
750251b2 Andy McCall 1 month ago ↶
1229
assert_eq! ( ( h, w) , ( vec! [ ObjectRef( [ 1 ; 32 ] ) ] , vec! [ ObjectRef( [ 2 ; 32 ] ) ] ) ) ;
75167672 Andy McCall 1 month ago
1230
assert_eq! (
1231
wire:: decode_hashes( & wire:: encode_hashes( & [ c.hash ( ) ] ) ) .unwrap ( ) ,
1232
vec! [ c.hash ( ) ]
1233
) ;
1234
assert_eq! (
1235
wire:: decode_fetch( & wire:: encode_fetch( & [ c.hash ( ) ] , Some ( 4096 ) ) ) .unwrap ( ) ,
1236
( vec! [ c.hash ( ) ] , Some ( 4096 ) )
1237
) ;
1238
assert_eq! (
1239
wire:: decode_fetch( & wire:: encode_fetch( & [ ] , None ) ) .unwrap ( ) ,
1240
( vec! [ ] , None )
1241
) ;
1242
assert_eq! (
1243
wire:: decode_shallow( & wire:: encode_shallow( & [ c.hash ( ) ] , 3 ) ) .unwrap ( ) ,
1244
( vec! [ c.hash ( ) ] , 3 )
1245
) ;
b3c5a59a Andy McCall 1 month ago ↶
1246
75167672 Andy McCall 1 month ago
1247
assert_eq! (
1248
wire:: decode_fetch( & wire:: encode_hashes( & [ c.hash ( ) ] ) ) .unwrap ( ) ,
1249
( vec! [ c.hash ( ) ] , None )
1250
) ;
750251b2 Andy McCall 1 month ago ↶
1251
75167672 Andy McCall 1 month ago
1252
let up = RefUpdate {
1253
name: " main" .into ( ) ,
1254
old_state: ObjectRef( [ 1 ; 32 ] ) ,
1255
new_state: ObjectRef( [ 2 ; 32 ] ) ,
1256
force: true ,
1257
attestation: Some ( SignedAttest {
1258
attest: att,
1259
sigs: vec! [ sig] ,
1260
} ) ,
1261
} ;
1262
let sp = SignedPolicy {
1263
policy,
1264
sigs: vec! [ psig] ,
1265
} ;
1266
let ( b2, ups, pol, tags) = wire:: decode_push( & wire:: encode_push(
1267
& bundle,
1268
std:: slice:: from_ref( & up) ,
1269
Some ( & sp) ,
1270
std:: slice:: from_ref( & tag) ,
1271
) )
1272
.unwrap ( ) ;
83e0677b Andy McCall 1 month ago ↶
1273
assert_eq! ( pol, Some ( sp) ) ;
132b884b Andy McCall 1 month ago ↶
1274
assert_eq! ( tags, vec! [ tag] ) ;
750251b2 Andy McCall 1 month ago ↶
1275
assert_eq! ( b2.changes.len ( ) , 1 ) ;
1276
assert_eq! ( ups[ 0 ] .name, " main" ) ;
1277
assert! ( ups[ 0 ] .force) ;
1278
assert_eq! ( ups[ 0 ] .attestation, up.attestation) ;
75167672 Andy McCall 1 month ago
1279
let res = vec! [ RefResult {
1280
name: " main" .into ( ) ,
1281
ok: false ,
1282
reason: " stale" .into ( ) ,
1283
} ] ;
1284
assert_eq! (
1285
wire:: decode_push_response( & wire:: encode_push_response( & res) ) .unwrap ( ) ,
1286
res
1287
) ;
1288
assert_eq! (
1289
wire:: decode_capabilities( & wire:: encode_capabilities( [ 7 ; 16 ] ) ) .unwrap ( ) ,
1290
[ 7 ; 16 ]
1291
) ;
750251b2 Andy McCall 1 month ago ↶
1292
}
1293
}