1
+ use std:: sync:: Arc ;
2
+ use std:: time:: Duration ;
3
+
1
4
use anyhow:: { anyhow, Error } ;
2
5
use bytes:: Bytes ;
3
6
use graph:: futures03:: future:: BoxFuture ;
4
- use graph:: {
5
- derive:: CheapClone ,
6
- ipfs_client:: { CidFile , IpfsClient } ,
7
- prelude:: CheapClone ,
8
- } ;
9
- use std:: time:: Duration ;
7
+ use graph:: ipfs:: ContentPath ;
8
+ use graph:: ipfs:: IpfsClient ;
9
+ use graph:: ipfs:: IpfsError ;
10
+ use graph:: { derive:: CheapClone , prelude:: CheapClone } ;
10
11
use tower:: { buffer:: Buffer , ServiceBuilder , ServiceExt } ;
11
12
12
- const CLOUDFLARE_TIMEOUT : u16 = 524 ;
13
- const GATEWAY_TIMEOUT : u16 = 504 ;
14
-
15
- pub type IpfsService = Buffer < CidFile , BoxFuture < ' static , Result < Option < Bytes > , Error > > > ;
13
+ pub type IpfsService = Buffer < ContentPath , BoxFuture < ' static , Result < Option < Bytes > , Error > > > ;
16
14
17
15
pub fn ipfs_service (
18
- client : IpfsClient ,
16
+ client : Arc < dyn IpfsClient > ,
19
17
max_file_size : usize ,
20
18
timeout : Duration ,
21
19
rate_limit : u16 ,
22
20
) -> IpfsService {
23
21
let ipfs = IpfsServiceInner {
24
22
client,
25
- max_file_size,
26
23
timeout,
24
+ max_file_size,
27
25
} ;
28
26
29
27
let svc = ServiceBuilder :: new ( )
@@ -38,37 +36,30 @@ pub fn ipfs_service(
38
36
39
37
#[ derive( Clone , CheapClone ) ]
40
38
struct IpfsServiceInner {
41
- client : IpfsClient ,
42
- max_file_size : usize ,
39
+ client : Arc < dyn IpfsClient > ,
43
40
timeout : Duration ,
41
+ max_file_size : usize ,
44
42
}
45
43
46
44
impl IpfsServiceInner {
47
- async fn call_inner ( self , req : CidFile ) -> Result < Option < Bytes > , Error > {
48
- let CidFile { cid, path } = req;
49
- let multihash = cid. hash ( ) . code ( ) ;
45
+ async fn call_inner ( self , path : ContentPath ) -> Result < Option < Bytes > , Error > {
46
+ let multihash = path. cid ( ) . hash ( ) . code ( ) ;
50
47
if !SAFE_MULTIHASHES . contains ( & multihash) {
51
48
return Err ( anyhow ! ( "CID multihash {} is not allowed" , multihash) ) ;
52
49
}
53
50
54
- let cid_str = match path {
55
- Some ( path) => format ! ( "{}/{}" , cid, path) ,
56
- None => cid. to_string ( ) ,
57
- } ;
58
-
59
51
let res = self
60
52
. client
61
- . cat_all ( & cid_str , Some ( self . timeout ) , self . max_file_size )
53
+ . cat ( & path , self . max_file_size , Some ( self . timeout ) )
62
54
. await ;
63
55
64
56
match res {
65
57
Ok ( file_bytes) => Ok ( Some ( file_bytes) ) ,
66
- Err ( e) => match e. status ( ) . map ( |e| e. as_u16 ( ) ) {
67
- // Timeouts in IPFS mean the file is not available, so we return `None`
68
- Some ( GATEWAY_TIMEOUT ) | Some ( CLOUDFLARE_TIMEOUT ) => return Ok ( None ) ,
69
- _ if e. is_timeout ( ) => return Ok ( None ) ,
70
- _ => return Err ( e. into ( ) ) ,
71
- } ,
58
+ Err ( IpfsError :: RequestFailed ( err) ) if err. is_timeout ( ) => {
59
+ // Timeouts in IPFS mean that the content is not available, so we return `None`.
60
+ Ok ( None )
61
+ }
62
+ Err ( err) => Err ( err. into ( ) ) ,
72
63
}
73
64
}
74
65
}
@@ -96,48 +87,42 @@ const SAFE_MULTIHASHES: [u64; 15] = [
96
87
97
88
#[ cfg( test) ]
98
89
mod test {
99
- use ipfs:: IpfsApi ;
100
- use ipfs_api as ipfs;
101
- use std:: { fs, str:: FromStr , time:: Duration } ;
90
+ use std:: time:: Duration ;
91
+
92
+ use graph:: components:: link_resolver:: ArweaveClient ;
93
+ use graph:: components:: link_resolver:: ArweaveResolver ;
94
+ use graph:: data:: value:: Word ;
95
+ use graph:: ipfs:: test_utils:: add_files_to_local_ipfs_node_for_testing;
96
+ use graph:: ipfs:: IpfsRpcClient ;
97
+ use graph:: ipfs:: ServerAddress ;
98
+ use graph:: tokio;
102
99
use tower:: ServiceExt ;
103
-
104
- use cid:: Cid ;
105
- use graph:: {
106
- components:: link_resolver:: { ArweaveClient , ArweaveResolver } ,
107
- data:: value:: Word ,
108
- ipfs_client:: IpfsClient ,
109
- tokio,
110
- } ;
111
-
112
100
use uuid:: Uuid ;
113
101
102
+ use super :: * ;
103
+
114
104
#[ tokio:: test]
115
105
async fn cat_file_in_folder ( ) {
116
- let path = "./tests/fixtures/ipfs_folder" ;
117
- let uid = Uuid :: new_v4 ( ) . to_string ( ) ;
118
- fs:: write ( format ! ( "{}/random.txt" , path) , & uid) . unwrap ( ) ;
106
+ let random_bytes = Uuid :: new_v4 ( ) . as_bytes ( ) . to_vec ( ) ;
107
+ let ipfs_file = ( "dir/file.txt" , random_bytes. clone ( ) ) ;
119
108
120
- let cl: ipfs:: IpfsClient = ipfs:: IpfsClient :: default ( ) ;
109
+ let add_resp = add_files_to_local_ipfs_node_for_testing ( [ ipfs_file] )
110
+ . await
111
+ . unwrap ( ) ;
121
112
122
- let rsp = cl . add_path ( path ) . await . unwrap ( ) ;
113
+ let dir_cid = add_resp . into_iter ( ) . find ( |x| x . name == "dir" ) . unwrap ( ) . hash ;
123
114
124
- let ipfs_folder = rsp. iter ( ) . find ( |rsp| rsp. name == "ipfs_folder" ) . unwrap ( ) ;
115
+ let client =
116
+ IpfsRpcClient :: new_unchecked ( ServerAddress :: local_rpc_api ( ) , & graph:: log:: discard ( ) )
117
+ . unwrap ( )
118
+ . into_boxed ( ) ;
125
119
126
- let local = IpfsClient :: localhost ( ) ;
127
- let cid = Cid :: from_str ( & ipfs_folder. hash ) . unwrap ( ) ;
128
- let file = "random.txt" . to_string ( ) ;
120
+ let svc = ipfs_service ( client. into ( ) , 100000 , Duration :: from_secs ( 30 ) , 10 ) ;
129
121
130
- let svc = super :: ipfs_service ( local, 100000 , Duration :: from_secs ( 5 ) , 10 ) ;
122
+ let path = ContentPath :: new ( format ! ( "{dir_cid}/file.txt" ) ) . unwrap ( ) ;
123
+ let content = svc. oneshot ( path) . await . unwrap ( ) . unwrap ( ) ;
131
124
132
- let content = svc
133
- . oneshot ( super :: CidFile {
134
- cid,
135
- path : Some ( file) ,
136
- } )
137
- . await
138
- . unwrap ( )
139
- . unwrap ( ) ;
140
- assert_eq ! ( content. to_vec( ) , uid. as_bytes( ) . to_vec( ) ) ;
125
+ assert_eq ! ( content. to_vec( ) , random_bytes) ;
141
126
}
142
127
143
128
#[ tokio:: test]
0 commit comments