Skip to content

Commit 57269c7

Browse files
authored
Merge pull request #170 from ipfs-shipyard/fix/ipfs-v0.5.0
Update to ipfs v0.5.0
2 parents b5f6b90 + e909b6e commit 57269c7

16 files changed

+126
-74
lines changed

.github/workflows/ant.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Java CI
2+
3+
on: [push]
4+
5+
jobs:
6+
build:
7+
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- uses: actions/checkout@v1
12+
- name: Set up JDK 11
13+
uses: actions/setup-java@v1
14+
with:
15+
java-version: 11
16+
- name: Install and run ipfs
17+
run: ./install-run-ipfs.sh
18+
- name: Build with Ant
19+
run: ant -noinput -buildfile build.xml dist
20+
- name: Run tests
21+
timeout-minutes: 10
22+
run: ant -noinput -buildfile build.xml test

.travis.yml

-11
This file was deleted.

build.xml

+10-7
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,29 @@
4040
<attribute name="Class-Path" value="${manifest_cp}"/>
4141
<attribute name="Implementation-Vendor" value="io.ipfs"/>
4242
<attribute name="Implementation-Title" value="api"/>
43-
<attribute name="Implementation-Version" value="1.2.0"/>
43+
<attribute name="Implementation-Version" value="1.3.0"/>
4444
</manifest>
4545
</jar>
4646
</target>
4747

4848
<target name="test" depends="compile,dist">
49-
<junit printsummary="yes" fork="true" haltonfailure="yes">
49+
<junit printsummary="yes" fork="true">
5050
<jvmarg value="-Xmx1g"/>
5151
<classpath>
52-
<pathelement location="lib/junit-4.11.jar" />
52+
<pathelement location="lib/junit-4.12.jar" />
5353
<pathelement location="lib/hamcrest-core-1.3.jar" />
54-
<pathelement location="lib/multihash.jar" />
55-
<pathelement location="lib/multiaddr.jar" />
5654
<pathelement location="dist/ipfs.jar" />
5755
</classpath>
58-
<test name="io.ipfs.api.APITest" haltonfailure="yes">
56+
<batchtest haltonfailure="yes">
57+
<fileset dir="src/test/java">
58+
</fileset>
5959
<formatter type="plain"/>
6060
<formatter type="xml"/>
61-
</test>
61+
</batchtest>
6262
</junit>
63+
<exec executable="./print_test_errors.sh" failonerror="true">
64+
<arg value="./TEST*"/>
65+
</exec>
6366
</target>
6467

6568
<target name="clean" description="clean up">

docker-compose.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: '2'
22
services:
33
ipfs-daemon:
4-
image: 'ipfs/go-ipfs:v0.4.16'
4+
image: 'ipfs/go-ipfs:v0.6.0'
55
ports:
66
- "4001:4001"
77
- "5001:5001"

install-run-ipfs.sh

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#! /bin/sh
2+
wget https://dist.ipfs.io/go-ipfs/v0.6.0/go-ipfs_v0.6.0_linux-amd64.tar.gz -O /tmp/go-ipfs_linux-amd64.tar.gz
3+
tar -xvf /tmp/go-ipfs_linux-amd64.tar.gz
4+
export PATH=$PATH:$PWD/go-ipfs/
5+
ipfs init
6+
ipfs daemon --enable-pubsub-experiment --routing=dhtclient &

lib/cid.jar

-68 Bytes
Binary file not shown.

lib/multiaddr.jar

420 Bytes
Binary file not shown.

lib/multibase.jar

18.2 KB
Binary file not shown.

lib/multihash.jar

3.86 KB
Binary file not shown.

pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>com.github.ipfs</groupId>
66
<artifactId>java-ipfs-http-client</artifactId>
7-
<version>v1.2.3</version>
7+
<version>v1.3.0</version>
88
<packaging>jar</packaging>
99

1010
<name>java-ipfs-http-client</name>
@@ -34,7 +34,7 @@
3434
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
3535
<version.junit>4.12</version.junit>
3636
<version.hamcrest>1.3</version.hamcrest>
37-
<version.multiaddr>v1.3.1</version.multiaddr>
37+
<version.multiaddr>v1.4.1</version.multiaddr>
3838
</properties>
3939

4040
<repositories>

print_test_errors.sh

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
#
3+
# Read junit test-reports and print a summary of the error-cases, including the stack trace.
4+
# Will exit with status 1 if there are any errors, otherwise exit status 0.
5+
#
6+
# By default will scan all files in "./test.reports".
7+
#
8+
# Usage "./print_test_errors.sh <test-report-path>
9+
#
10+
awk '/<(failure|error)/,/\/(failure|error)/ {print prev; has_err=1} {prev=$0} END {exit has_err}' ${1:-test.reports/*}

src/main/java/io/ipfs/api/IPFS.java

+59-25
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ public class IPFS {
1818
public enum PinType {all, direct, indirect, recursive}
1919
public List<String> ObjectTemplates = Arrays.asList("unixfs-dir");
2020
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;
2223

2324
public final String host;
2425
public final int port;
2526
public final String protocol;
2627
private final String version;
27-
private int timeout = DEFAULT_TIMEOUT;
28+
private final int connectTimeoutMillis;
29+
private final int readTimeoutMillis;
2830
public final Key key = new Key();
2931
public final Pin pin = new Pin();
3032
public final Repo repo = new Repo();
@@ -56,10 +58,18 @@ public IPFS(MultiAddress addr) {
5658
}
5759

5860
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");
5967
this.host = host;
6068
this.port = port;
69+
this.connectTimeoutMillis = connectTimeoutMillis;
70+
this.readTimeoutMillis = readTimeoutMillis;
6171

62-
if(ssl) {
72+
if (ssl) {
6373
this.protocol = "https";
6474
} else {
6575
this.protocol = "http";
@@ -82,9 +92,7 @@ public IPFS(String host, int port, String version, boolean ssl) {
8292
* @return current IPFS object with configured timeout
8393
*/
8494
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"));
8896
}
8997

9098
public List<MerkleNode> add(NamedStreamable file) throws IOException {
@@ -206,10 +214,10 @@ public List<Multihash> rm(Multihash hash, boolean recursive) throws IOException
206214
return ((List<Object>) json.get("Pins")).stream().map(x -> Cid.decode((String) x)).collect(Collectors.toList());
207215
}
208216

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 {
210218
return ((List<Object>)((Map)retrieveAndParse("pin/update?stream-channels=true&arg=" + existing + "&arg=" + modified + "&unpin=" + unpin)).get("Pins"))
211219
.stream()
212-
.map(x -> new MultiAddress((String) x))
220+
.map(x -> Cid.decode((String) x))
213221
.collect(Collectors.toList());
214222
}
215223
}
@@ -300,6 +308,10 @@ public byte[] get(Multihash hash) throws IOException {
300308
return retrieve("block/get?stream-channels=true&arg=" + hash);
301309
}
302310

311+
public byte[] rm(Multihash hash) throws IOException {
312+
return retrieve("block/rm?stream-channels=true&arg=" + hash);
313+
}
314+
303315
public List<MerkleNode> put(List<byte[]> data) throws IOException {
304316
return put(data, Optional.empty());
305317
}
@@ -672,13 +684,37 @@ private void retrieveAndParseStream(String path, Consumer<Object> results, Consu
672684

673685
private byte[] retrieve(String path) throws IOException {
674686
URL target = new URL(protocol, host, port, version + path);
675-
return IPFS.get(target, timeout);
687+
return IPFS.get(target, connectTimeoutMillis, readTimeoutMillis);
676688
}
677689

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");
680712

681713
try {
714+
OutputStream out = conn.getOutputStream();
715+
out.write(new byte[0]);
716+
out.flush();
717+
out.close();
682718
InputStream in = conn.getInputStream();
683719
ByteArrayOutputStream resp = new ByteArrayOutputStream();
684720

@@ -689,13 +725,10 @@ private static byte[] get(URL target, int timeout) throws IOException {
689725
return resp.toByteArray();
690726
} catch (ConnectException e) {
691727
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));
694728
} 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.\nTrailer: " + 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+"\nTrailer: " + conn.getHeaderFields().get("Trailer"), e);
699732
}
700733
}
701734

@@ -740,21 +773,21 @@ private List<Object> getAndParseStream(String path) throws IOException {
740773

741774
private InputStream retrieveStream(String path) throws IOException {
742775
URL target = new URL(protocol, host, port, version + path);
743-
return IPFS.getStream(target, timeout);
776+
return IPFS.getStream(target, connectTimeoutMillis, readTimeoutMillis);
744777
}
745778

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);
748781
return conn.getInputStream();
749782
}
750783

751784
private Map postMap(String path, byte[] body, Map<String, String> headers) throws IOException {
752785
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)));
754787
}
755788

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);
758791
for (String key: headers.keySet())
759792
conn.setRequestProperty(key, headers.get(key));
760793
conn.setDoOutput(true);
@@ -785,11 +818,12 @@ private static boolean detectSSL(MultiAddress multiaddress) {
785818
return multiaddress.toString().contains("/https");
786819
}
787820

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 {
789822
HttpURLConnection conn = (HttpURLConnection) target.openConnection();
790823
conn.setRequestMethod(method);
791824
conn.setRequestProperty("Content-Type", "application/json");
792-
conn.setReadTimeout(timeout);
825+
conn.setConnectTimeout(connectTimeoutMillis);
826+
conn.setReadTimeout(readTimeoutMillis);
793827
return conn;
794828
}
795829
}

src/main/java/io/ipfs/api/IpldNode.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ default Cid cid() {
3030
MessageDigest md = MessageDigest.getInstance("SHA-256");
3131
md.update(raw);
3232
byte[] digest = md.digest();
33-
Multihash h = new Multihash(Multihash.Type.sha2_256, digest);
34-
return new Cid(1, Cid.Codec.DagCbor, h);
33+
return new Cid(1, Cid.Codec.DagCbor, Multihash.Type.sha2_256, digest);
3534
} catch (NoSuchAlgorithmException e) {
3635
throw new RuntimeException(e.getMessage(), e);
3736
}

src/main/java/io/ipfs/api/MerkleNode.java

-8
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,8 @@ public static MerkleNode fromJSON(Object rawjson) {
8787

8888
public Object toJSON() {
8989
Map<String, Object> res = new TreeMap<>();
90-
res.put("Hash", hash);
9190
res.put("Links", links.stream().map(x -> x.hash).collect(Collectors.toList()));
9291
data.ifPresent(bytes -> res.put("Data", bytes));
93-
name.ifPresent(s -> res.put("Name", s));
94-
if (size.isPresent()) {
95-
res.put("Size", size.get());
96-
} else {
97-
largeSize.ifPresent(s -> res.put("Size", s));
98-
}
99-
type.ifPresent(integer -> res.put("Type", integer));
10092
return res;
10193
}
10294

src/test/java/io/ipfs/api/APITest.java

+14-15
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ public void wrappedSingleFileTest() throws IOException {
102102

103103
@Test
104104
public void dirTest() throws IOException {
105-
NamedStreamable dir = new NamedStreamable.FileWrapper(new File("java"));
105+
Path test = Files.createTempDirectory("test");
106+
Files.write(test.resolve("file.txt"), "G'day IPFS!".getBytes());
107+
NamedStreamable dir = new NamedStreamable.FileWrapper(test.toFile());
106108
List<MerkleNode> add = ipfs.add(dir);
107109
MerkleNode addResult = add.get(add.size() - 1);
108110
List<MerkleNode> ls = ipfs.ls(addResult.hash);
@@ -137,16 +139,16 @@ public void directoryTest() throws IOException {
137139
List<MerkleNode> addParts = ipfs.add(new NamedStreamable.FileWrapper(tmpDir.toFile()));
138140
MerkleNode addResult = addParts.get(addParts.size() - 1);
139141
List<MerkleNode> lsResult = ipfs.ls(addResult.hash);
140-
if (lsResult.size() != 1)
142+
if (lsResult.size() != 2)
141143
throw new IllegalStateException("Incorrect number of objects in ls!");
142-
if (!lsResult.get(0).equals(addResult))
143-
throw new IllegalStateException("Object not returned in ls!");
144+
if (! lsResult.stream().map(x -> x.name.get()).collect(Collectors.toSet()).equals(Set.of(subdirName, fileName)))
145+
throw new IllegalStateException("Dir not returned in ls!");
144146
byte[] catResult = ipfs.cat(addResult.hash, "/" + fileName);
145-
if (!Arrays.equals(catResult, fileContents))
147+
if (! Arrays.equals(catResult, fileContents))
146148
throw new IllegalStateException("Different contents!");
147149

148150
byte[] catResult2 = ipfs.cat(addResult.hash, "/" + subdirName + "/" + subfileName);
149-
if (!Arrays.equals(catResult2, file2Contents))
151+
if (! Arrays.equals(catResult2, file2Contents))
150152
throw new IllegalStateException("Different contents!");
151153
}
152154

@@ -248,7 +250,7 @@ public void pinUpdate() throws IOException {
248250

249251
CborObject.CborList root2 = new CborObject.CborList(Arrays.asList(new CborObject.CborMerkleLink(hashChild1), new CborObject.CborLong(42)));
250252
MerkleNode root2Res = ipfs.block.put(Collections.singletonList(root2.toByteArray()), Optional.of("cbor")).get(0);
251-
List<MultiAddress> update = ipfs.pin.update(root1Res.hash, root2Res.hash, true);
253+
List<Multihash> update = ipfs.pin.update(root1Res.hash, root2Res.hash, true);
252254

253255
Map<Multihash, Object> ls = ipfs.pin.ls(IPFS.PinType.all);
254256
boolean childPresent = ls.containsKey(hashChild1);
@@ -282,7 +284,7 @@ public void rawLeafNodePinUpdate() throws IOException {
282284
new CborObject.CborLong(42))
283285
);
284286
MerkleNode root2Res = ipfs.block.put(Collections.singletonList(root2.toByteArray()), Optional.of("cbor")).get(0);
285-
List<MultiAddress> update = ipfs.pin.update(root1Res.hash, root2Res.hash, false);
287+
List<Multihash> update = ipfs.pin.update(root1Res.hash, root2Res.hash, false);
286288
}
287289

288290
@Test
@@ -383,6 +385,7 @@ public void bulkBlockTest() throws IOException {
383385
System.out.println();
384386
}
385387

388+
@Ignore // Ignored because ipfs frequently times out internally in the publish call
386389
@Test
387390
public void publish() throws Exception {
388391
// JSON document
@@ -415,18 +418,14 @@ public void pubsubSynchronous() throws Exception {
415418
throw new RuntimeException(e);}
416419
}).start();
417420

418-
long start = System.currentTimeMillis();
419-
for (int i=1; i < 100; ) {
420-
long t1 = System.currentTimeMillis();
421+
int nMessages = 100;
422+
for (int i = 1; i < nMessages; ) {
421423
ipfs.pubsub.pub(topic, "Hello!");
422424
if (res.size() >= i) {
423-
long t2 = System.currentTimeMillis();
424-
System.out.println("pub => sub took " + (t2 - t1));
425425
i++;
426426
}
427427
}
428-
long duration = System.currentTimeMillis() - start;
429-
Assert.assertTrue("Fast synchronous pub-sub", duration < 1000);
428+
Assert.assertTrue(res.size() > nMessages - 5); // pubsub is not reliable so it loses messages
430429
}
431430

432431
@Test

0 commit comments

Comments
 (0)