1 package net.pterodactylus.fcp.quelaton;
3 import java.io.IOException;
4 import java.util.concurrent.ExecutionException;
5 import java.util.concurrent.ExecutorService;
6 import java.util.concurrent.Future;
7 import java.util.concurrent.atomic.AtomicBoolean;
8 import java.util.concurrent.atomic.AtomicReference;
9 import java.util.function.Supplier;
11 import net.pterodactylus.fcp.ClientHello;
12 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
13 import net.pterodactylus.fcp.FcpConnection;
14 import net.pterodactylus.fcp.FcpKeyPair;
15 import net.pterodactylus.fcp.GenerateSSK;
16 import net.pterodactylus.fcp.NodeHello;
17 import net.pterodactylus.fcp.SSKKeypair;
20 * Default {@link FcpClient} implementation.
22 * @author <a href="bombe@freenetproject.org">David ‘Bombe’ Roden</a>
24 public class DefaultFcpClient implements FcpClient {
26 private final ExecutorService threadPool;
27 private final String hostname;
28 private final int port;
29 private final AtomicReference<FcpConnection> fcpConnection = new AtomicReference<>();
30 private final Supplier<String> clientName;
31 private final Supplier<String> expectedVersion;
33 public DefaultFcpClient(ExecutorService threadPool, String hostname, int port, Supplier<String> clientName,
34 Supplier<String> expectedVersion) {
35 this.threadPool = threadPool;
36 this.hostname = hostname;
38 this.clientName = clientName;
39 this.expectedVersion = expectedVersion;
42 private void connect() throws IOException {
43 if (fcpConnection.get() != null) {
46 fcpConnection.compareAndSet(null, createConnection());
49 private FcpConnection createConnection() throws IOException {
50 FcpConnection connection = new FcpConnection(hostname, port);
52 AtomicReference<NodeHello> receivedNodeHello = new AtomicReference<>();
53 AtomicBoolean receivedClosed = new AtomicBoolean();
54 FcpReplySequence nodeHelloSequence = new FcpReplySequence(threadPool, connection);
56 .handle(NodeHello.class)
57 .with((nodeHello) -> receivedNodeHello.set(nodeHello));
59 .handle(CloseConnectionDuplicateClientName.class)
60 .with((closeConnection) -> receivedClosed.set(true));
61 nodeHelloSequence.waitFor(() -> receivedNodeHello.get() != null || receivedClosed.get());
62 ClientHello clientHello = new ClientHello(clientName.get(), expectedVersion.get());
64 nodeHelloSequence.send(clientHello).get();
65 } catch (InterruptedException | ExecutionException e) {
67 throw new IOException(String.format("Could not connect to %s:%d.", hostname, port), e);
73 public GenerateKeypairCommand generateKeypair() {
74 return new GenerateKeypairCommandImpl();
77 private class GenerateKeypairCommandImpl implements GenerateKeypairCommand {
80 public Future<FcpKeyPair> execute() {
81 return threadPool.submit(() -> {
83 GenerateSSK generateSSK = new GenerateSSK();
84 AtomicReference<FcpKeyPair> keyPair = new AtomicReference<>();
85 FcpReplySequence replySequence = new FcpReplySequence(threadPool, fcpConnection.get());
86 replySequence.handle(SSKKeypair.class)
87 .with((message) -> keyPair.set(
88 new FcpKeyPair(message.getRequestURI(), message.getInsertURI())));
89 replySequence.waitFor(() -> keyPair.get() != null);
90 replySequence.send(generateSSK).get();