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