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