11use data:: subgraph:: Link ;
22use failure;
33use ipfs_api;
4+ use std:: env;
5+ use std:: str:: FromStr ;
6+ use std:: time:: Duration ;
47use tokio:: prelude:: * ;
58
6- use std :: time :: Duration ;
9+ const MAX_IPFS_FILE_BYTES_ENV_VAR : & str = "GRAPH_MAX_IPFS_FILE_BYTES" ;
710
811/// Resolves links to subgraph manifests and resources referenced by them.
912pub trait LinkResolver : Send + Sync + ' static {
@@ -12,18 +15,48 @@ pub trait LinkResolver: Send + Sync + 'static {
1215}
1316
1417impl LinkResolver for ipfs_api:: IpfsClient {
15- /// Currently supports only links of the form `/ipfs/ipfs_hash`
18+ /// Supports links of the form `/ipfs/ipfs_hash` or just `ipfs_hash`.
1619 fn cat ( & self , link : & Link ) -> Box < Future < Item = Vec < u8 > , Error = failure:: Error > + Send > {
1720 // Discard the `/ipfs/` prefix (if present) to get the hash.
18- let path = link. link . trim_left_matches ( "/ipfs/" ) ;
21+ let path = link. link . trim_left_matches ( "/ipfs/" ) . to_owned ( ) ;
22+ let max_file_bytes = env:: var ( MAX_IPFS_FILE_BYTES_ENV_VAR )
23+ . ok ( )
24+ . and_then ( |s| u64:: from_str ( & s) . ok ( ) ) ;
25+
26+ let cat = self
27+ . cat ( & path)
28+ . concat2 ( )
29+ // Guard against IPFS unresponsiveness.
30+ . timeout ( Duration :: from_secs ( 10 ) )
31+ . map ( |x| x. to_vec ( ) )
32+ . map_err ( |e| failure:: err_msg ( e. to_string ( ) ) ) ;
1933
20- Box :: new (
21- self . cat ( path)
22- . concat2 ( )
23- // Guard against IPFS unresponsiveness.
24- . timeout ( Duration :: from_secs ( 10 ) )
25- . map ( |x| x. to_vec ( ) )
26- . map_err ( |e| failure:: err_msg ( e. to_string ( ) ) ) ,
27- )
34+ match max_file_bytes {
35+ Some ( max_bytes) => Box :: new (
36+ self . object_stat ( & path)
37+ . map_err ( |e| failure:: err_msg ( e. to_string ( ) ) )
38+ . and_then ( move |stat| match stat. cumulative_size > max_bytes {
39+ false => Ok ( ( ) ) ,
40+ true => Err ( format_err ! ( "Ipfs file {} is too large" , path) ) ,
41+ } )
42+ . and_then ( |( ) | cat) ,
43+ ) ,
44+ None => Box :: new ( cat) ,
45+ }
2846 }
2947}
48+
49+ #[ test]
50+ fn max_file_size ( ) {
51+ env:: set_var ( MAX_IPFS_FILE_BYTES_ENV_VAR , "200" ) ;
52+ let file: & [ u8 ] = & [ 0u8 ; 201 ] ;
53+ let client = ipfs_api:: IpfsClient :: default ( ) ;
54+
55+ let mut runtime = tokio:: runtime:: Runtime :: new ( ) . unwrap ( ) ;
56+ let link = runtime. block_on ( client. add ( file) ) . unwrap ( ) . hash ;
57+ let err = runtime
58+ . block_on ( LinkResolver :: cat ( & client, & Link { link : link. clone ( ) } ) )
59+ . unwrap_err ( ) ;
60+ env:: remove_var ( MAX_IPFS_FILE_BYTES_ENV_VAR ) ;
61+ assert_eq ! ( err. to_string( ) , format!( "Ipfs file {} is too large" , link) ) ;
62+ }
0 commit comments