@@ -18,13 +18,15 @@ public class IPFS {
18
18
public enum PinType {all , direct , indirect , recursive }
19
19
public List <String > ObjectTemplates = Arrays .asList ("unixfs-dir" );
20
20
public List <String > ObjectPatchTypes = Arrays .asList ("add-link" , "rm-link" , "set-data" , "append-data" );
21
- private static final int DEFAULT_TIMEOUT = 0 ;
21
+ private static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 10_000 ;
22
+ private static final int DEFAULT_READ_TIMEOUT_MILLIS = 60_000 ;
22
23
23
24
public final String host ;
24
25
public final int port ;
25
26
public final String protocol ;
26
27
private final String version ;
27
- private int timeout = DEFAULT_TIMEOUT ;
28
+ private final int connectTimeoutMillis ;
29
+ private final int readTimeoutMillis ;
28
30
public final Key key = new Key ();
29
31
public final Pin pin = new Pin ();
30
32
public final Repo repo = new Repo ();
@@ -56,10 +58,18 @@ public IPFS(MultiAddress addr) {
56
58
}
57
59
58
60
public IPFS (String host , int port , String version , boolean ssl ) {
61
+ this (host , port , version , DEFAULT_CONNECT_TIMEOUT_MILLIS , DEFAULT_READ_TIMEOUT_MILLIS , ssl );
62
+ }
63
+
64
+ public IPFS (String host , int port , String version , int connectTimeoutMillis , int readTimeoutMillis , boolean ssl ) {
65
+ if (connectTimeoutMillis < 0 ) throw new IllegalArgumentException ("connect timeout must be zero or positive" );
66
+ if (readTimeoutMillis < 0 ) throw new IllegalArgumentException ("read timeout must be zero or positive" );
59
67
this .host = host ;
60
68
this .port = port ;
69
+ this .connectTimeoutMillis = connectTimeoutMillis ;
70
+ this .readTimeoutMillis = readTimeoutMillis ;
61
71
62
- if (ssl ) {
72
+ if (ssl ) {
63
73
this .protocol = "https" ;
64
74
} else {
65
75
this .protocol = "http" ;
@@ -82,9 +92,7 @@ public IPFS(String host, int port, String version, boolean ssl) {
82
92
* @return current IPFS object with configured timeout
83
93
*/
84
94
public IPFS timeout (int timeout ) {
85
- if (timeout < 0 ) throw new IllegalArgumentException ("timeout must be zero or positive" );
86
- this .timeout = timeout ;
87
- return this ;
95
+ return new IPFS (host , port , version , connectTimeoutMillis , readTimeoutMillis , protocol .equals ("https" ));
88
96
}
89
97
90
98
public List <MerkleNode > add (NamedStreamable file ) throws IOException {
@@ -206,10 +214,10 @@ public List<Multihash> rm(Multihash hash, boolean recursive) throws IOException
206
214
return ((List <Object >) json .get ("Pins" )).stream ().map (x -> Cid .decode ((String ) x )).collect (Collectors .toList ());
207
215
}
208
216
209
- public List <MultiAddress > update (Multihash existing , Multihash modified , boolean unpin ) throws IOException {
217
+ public List <Multihash > update (Multihash existing , Multihash modified , boolean unpin ) throws IOException {
210
218
return ((List <Object >)((Map )retrieveAndParse ("pin/update?stream-channels=true&arg=" + existing + "&arg=" + modified + "&unpin=" + unpin )).get ("Pins" ))
211
219
.stream ()
212
- .map (x -> new MultiAddress ((String ) x ))
220
+ .map (x -> Cid . decode ((String ) x ))
213
221
.collect (Collectors .toList ());
214
222
}
215
223
}
@@ -300,6 +308,10 @@ public byte[] get(Multihash hash) throws IOException {
300
308
return retrieve ("block/get?stream-channels=true&arg=" + hash );
301
309
}
302
310
311
+ public byte [] rm (Multihash hash ) throws IOException {
312
+ return retrieve ("block/rm?stream-channels=true&arg=" + hash );
313
+ }
314
+
303
315
public List <MerkleNode > put (List <byte []> data ) throws IOException {
304
316
return put (data , Optional .empty ());
305
317
}
@@ -672,13 +684,37 @@ private void retrieveAndParseStream(String path, Consumer<Object> results, Consu
672
684
673
685
private byte [] retrieve (String path ) throws IOException {
674
686
URL target = new URL (protocol , host , port , version + path );
675
- return IPFS .get (target , timeout );
687
+ return IPFS .get (target , connectTimeoutMillis , readTimeoutMillis );
676
688
}
677
689
678
- private static byte [] get (URL target , int timeout ) throws IOException {
679
- HttpURLConnection conn = configureConnection (target , "GET" , timeout );
690
+ private static byte [] get (URL target , int connectTimeoutMillis , int readTimeoutMillis ) throws IOException {
691
+ HttpURLConnection conn = configureConnection (target , "POST" , connectTimeoutMillis , readTimeoutMillis );
692
+ conn .setDoOutput (true );
693
+ /* See IPFS commit for why this is a POST and not a GET https://github.com/ipfs/go-ipfs/pull/7097
694
+ This commit upgrades go-ipfs-cmds and configures the commands HTTP API Handler
695
+ to only allow POST/OPTIONS, disallowing GET and others in the handling of
696
+ command requests in the IPFS HTTP API (where before every type of request
697
+ method was handled, with GET/POST/PUT/PATCH being equivalent).
698
+
699
+ The Read-Only commands that the HTTP API attaches to the gateway endpoint will
700
+ additional handled GET as they did before (but stop handling PUT,DELETEs).
701
+
702
+ By limiting the request types we address the possibility that a website
703
+ accessed by a browser abuses the IPFS API by issuing GET requests to it which
704
+ have no Origin or Referrer set, and are thus bypass CORS and CSRF protections.
705
+
706
+ This is a breaking change for clients that relay on GET requests against the
707
+ HTTP endpoint (usually :5001). Applications integrating on top of the
708
+ gateway-read-only API should still work (including cross-domain access).
709
+ */
710
+ conn .setRequestMethod ("POST" );
711
+ conn .setRequestProperty ("Content-Type" , "application/json" );
680
712
681
713
try {
714
+ OutputStream out = conn .getOutputStream ();
715
+ out .write (new byte [0 ]);
716
+ out .flush ();
717
+ out .close ();
682
718
InputStream in = conn .getInputStream ();
683
719
ByteArrayOutputStream resp = new ByteArrayOutputStream ();
684
720
@@ -689,13 +725,10 @@ private static byte[] get(URL target, int timeout) throws IOException {
689
725
return resp .toByteArray ();
690
726
} catch (ConnectException e ) {
691
727
throw new RuntimeException ("Couldn't connect to IPFS daemon at " +target +"\n Is IPFS running?" );
692
- } catch (SocketTimeoutException e ) {
693
- throw new RuntimeException (String .format ("timeout (%d ms) has been exceeded" , timeout ));
694
728
} catch (IOException e ) {
695
- String err = Optional .ofNullable (conn .getErrorStream ())
696
- .map (s ->new String (readFully (s )))
697
- .orElse (e .getMessage ());
698
- throw new RuntimeException ("IOException contacting IPFS daemon.\n Trailer: " + conn .getHeaderFields ().get ("Trailer" ) + " " + err , e );
729
+ InputStream errorStream = conn .getErrorStream ();
730
+ String err = errorStream == null ? e .getMessage () : new String (readFully (errorStream ));
731
+ throw new RuntimeException ("IOException contacting IPFS daemon.\n " +err +"\n Trailer: " + conn .getHeaderFields ().get ("Trailer" ), e );
699
732
}
700
733
}
701
734
@@ -740,21 +773,21 @@ private List<Object> getAndParseStream(String path) throws IOException {
740
773
741
774
private InputStream retrieveStream (String path ) throws IOException {
742
775
URL target = new URL (protocol , host , port , version + path );
743
- return IPFS .getStream (target , timeout );
776
+ return IPFS .getStream (target , connectTimeoutMillis , readTimeoutMillis );
744
777
}
745
778
746
- private static InputStream getStream (URL target , int timeout ) throws IOException {
747
- HttpURLConnection conn = configureConnection (target , "GET " , timeout );
779
+ private static InputStream getStream (URL target , int connectTimeoutMillis , int readTimeoutMillis ) throws IOException {
780
+ HttpURLConnection conn = configureConnection (target , "POST " , connectTimeoutMillis , readTimeoutMillis );
748
781
return conn .getInputStream ();
749
782
}
750
783
751
784
private Map postMap (String path , byte [] body , Map <String , String > headers ) throws IOException {
752
785
URL target = new URL (protocol , host , port , version + path );
753
- return (Map ) JSONParser .parse (new String (post (target , body , headers , timeout )));
786
+ return (Map ) JSONParser .parse (new String (post (target , body , headers , connectTimeoutMillis , readTimeoutMillis )));
754
787
}
755
788
756
- private static byte [] post (URL target , byte [] body , Map <String , String > headers , int timeout ) throws IOException {
757
- HttpURLConnection conn = configureConnection (target , "POST" , timeout );
789
+ private static byte [] post (URL target , byte [] body , Map <String , String > headers , int connectTimeoutMillis , int readTimeoutMillis ) throws IOException {
790
+ HttpURLConnection conn = configureConnection (target , "POST" , connectTimeoutMillis , readTimeoutMillis );
758
791
for (String key : headers .keySet ())
759
792
conn .setRequestProperty (key , headers .get (key ));
760
793
conn .setDoOutput (true );
@@ -785,11 +818,12 @@ private static boolean detectSSL(MultiAddress multiaddress) {
785
818
return multiaddress .toString ().contains ("/https" );
786
819
}
787
820
788
- private static HttpURLConnection configureConnection (URL target , String method , int timeout ) throws IOException {
821
+ private static HttpURLConnection configureConnection (URL target , String method , int connectTimeoutMillis , int readTimeoutMillis ) throws IOException {
789
822
HttpURLConnection conn = (HttpURLConnection ) target .openConnection ();
790
823
conn .setRequestMethod (method );
791
824
conn .setRequestProperty ("Content-Type" , "application/json" );
792
- conn .setReadTimeout (timeout );
825
+ conn .setConnectTimeout (connectTimeoutMillis );
826
+ conn .setReadTimeout (readTimeoutMillis );
793
827
return conn ;
794
828
}
795
829
}
0 commit comments