package peergos.shared.storage;
import peergos.shared.cbor.*;
import peergos.shared.crypto.asymmetric.*;
import peergos.shared.io.ipfs.api.*;
import peergos.shared.io.ipfs.cid.*;
import peergos.shared.io.ipfs.multihash.*;
import peergos.shared.user.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
public interface ContentAddressedStorage {
int MAX_OBJECT_LENGTH = 1024*256;
default CompletableFuture<Multihash> put(PublicSigningKey writer, byte[] block) {
return put(writer, Arrays.asList(block)).thenApply(hashes -> hashes.get(0));
}
CompletableFuture<List<Multihash>> put(PublicSigningKey writer, List<byte[]> blocks);
CompletableFuture<Optional<CborObject>> get(Multihash object);
CompletableFuture<List<Multihash>> recursivePin(Multihash h);
CompletableFuture<List<Multihash>> recursiveUnpin(Multihash h);
CompletableFuture<List<Multihash>> getLinks(Multihash root);
CompletableFuture<Optional<Integer>> getSize(Multihash block);
class HTTP implements ContentAddressedStorage {
private final HttpPoster poster;
private final String apiPrefix = "api/v0/";
public HTTP(HttpPoster poster) {
this.poster = poster;
}
private static Multihash getObjectHash(Object rawJson) {
Map json = (Map)rawJson;
String hash = (String)json.get("Hash");
if (hash == null)
hash = (String)json.get("Key");
return Cid.decode(hash);
}
private static String encode(String component) {
try {
return URLEncoder.encode(component, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@Override
public CompletableFuture<List<Multihash>> put(PublicSigningKey writer, List<byte[]> blocks) {
return poster.postMultipart(apiPrefix + "block/put?format=cbor"
+ "&writer=" + encode(writer.toString()), blocks)
.thenApply(bytes -> JSONParser.parseStream(new String(bytes))
.stream()
.map(json -> getObjectHash(json))
.collect(Collectors.toList()));
}
@Override
public CompletableFuture<Optional<CborObject>> get(Multihash hash) {
return poster.get(apiPrefix + "block/get?stream-channels=true&arg=" + hash.toString())
.thenApply(raw -> raw.length == 0 ? Optional.empty() : Optional.of(CborObject.fromByteArray(raw)));
}
@Override
public CompletableFuture<List<Multihash>> recursivePin(Multihash hash) {
return poster.get(apiPrefix + "pin/add?stream-channels=true&arg=" + hash.toString())
.thenApply(this::getPins);
}
@Override
public CompletableFuture<List<Multihash>> recursiveUnpin(Multihash hash) {
return poster.get(apiPrefix + "pin/rm?stream-channels=true&r=true&arg=" + hash.toString())
.thenApply(this::getPins);
}
private List<Multihash> getPins(byte[] raw) {
Map res = (Map)JSONParser.parse(new String(raw));
List<String> pins = (List<String>)res.get("Pins");
return pins.stream().map(Cid::decode).collect(Collectors.toList());
}
@Override
public CompletableFuture<List<Multihash>> getLinks(Multihash block) {
return poster.get(apiPrefix + "refs?arg=" + block.toString())
.thenApply(raw -> JSONParser.parseStream(new String(raw))
.stream()
.map(obj -> (String) (((Map) obj).get("Ref")))
.map(Cid::decode)
.collect(Collectors.toList()));
}
@Override
public CompletableFuture<Optional<Integer>> getSize(Multihash block) {
return poster.get(apiPrefix + "block/stat?stream-channels=true&arg=" + block.toString())
.thenApply(raw -> Optional.of((Integer)((Map)JSONParser.parse(new String(raw))).get("Size")));
}
}
}