diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/PeerDao.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/PeerDao.java index 55d28e88bf4830f2fed7baf141ae14faa9b6c313..6e4ee26b1c244aa72f47b4b829e44ae50b952704 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/PeerDao.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/PeerDao.java @@ -44,7 +44,7 @@ public interface PeerDao extends EntityDao<String, Peer> { Long getMaxLastUpTime(String currencyId); - void updatePeersAsDown(String currencyId, long maxUpTime); + void updatePeersAsDown(String currencyId, long upTimeLimitInSec); boolean hasPeersUpWithApi(String currencyId, Set<EndpointApi> api); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryPeerDaoImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryPeerDaoImpl.java index f9a3f1407fc274d8b49da029c1b6956aeea77636..5e85b4074c04461173275a55d07b5b63fd6d0f0d 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryPeerDaoImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryPeerDaoImpl.java @@ -130,13 +130,11 @@ public class MemoryPeerDaoImpl implements PeerDao { } @Override - public void updatePeersAsDown(String currencyId, long upTimeLimit) { + public void updatePeersAsDown(String currencyId, long upTimeLimitInSec) { getPeersByCurrencyId(currencyId).stream() - .filter(peer -> peer.getStats() != null && peer.getStats().getLastUpTime() <= upTimeLimit) - .forEach(peer -> { - peer.getStats().setStatus(Peer.PeerStatus.DOWN); - }); + .filter(peer -> peer.getStats() != null && peer.getStats().getLastUpTime() <= upTimeLimitInSec) + .forEach(peer -> peer.getStats().setStatus(Peer.PeerStatus.DOWN)); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Endpoints.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Endpoints.java index f994af40c3c8345ae1e5073aa00406fdbcd1f05f..f96d15baaf50f46796454ea07c865dd030f2c4c9 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Endpoints.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Endpoints.java @@ -34,10 +34,10 @@ import java.util.regex.Pattern; */ public class Endpoints { - public static final String EP_END_REGEXP = "(?:[ ]+([a-z0-9-_]+[.][a-z0-9-_.]*))?(?:[ ]+([0-9.]+))?(?:[ ]+([0-9a-f:]+))?(?:[ ]+([0-9]+))(?:[ ]+(/[^/]+))?$"; + public static final String EP_END_REGEXP = "(?: ([a-z_][a-z0-9-_.ÄŸÄž]*))?(?: ([0-9.]+))?(?: ([0-9a-f:]+))?(?: ([0-9]+))(?: (/[^/]+))?$"; public static final String BMA_API_REGEXP = "^BASIC_MERKLED_API" + EP_END_REGEXP; public static final String BMAS_API_REGEXP = "^BMAS" + EP_END_REGEXP; - public static final String WS2P_API_REGEXP = "^WS2P[ ]+([a-z0-9]+)[ ]+" + EP_END_REGEXP; + public static final String WS2P_API_REGEXP = "^WS2P ([a-f0-9]{8})" + EP_END_REGEXP; public static final String OTHER_API_REGEXP = "^([A-Z_-]+)" + EP_END_REGEXP; private static Pattern bmaPattern = Pattern.compile(BMA_API_REGEXP); @@ -104,9 +104,9 @@ public class Endpoints { endpoint.ipv4 = word; } else if (InetAddressUtils.isIPv6Address(word)) { endpoint.ipv6 = word; - } else if (i == matcher.groupCount() || (i == matcher.groupCount() -1) && word.matches("\\d+")){ + } else if ((i == matcher.groupCount() || i == matcher.groupCount() -1) && word.matches("^\\d+$")){ endpoint.port = Integer.parseInt(word); - } else if (word.startsWith("/")) { + } else if (i == matcher.groupCount() && word.startsWith("/")) { endpoint.path = word; } else { endpoint.dns = word; diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/NetworkPeerings.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/NetworkPeerings.java new file mode 100644 index 0000000000000000000000000000000000000000..07a19749783467c0cfc2d49e98b4745bda23437a --- /dev/null +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/NetworkPeerings.java @@ -0,0 +1,110 @@ +package org.duniter.core.client.model.bma; + +/* + * #%L + * Duniter4j :: Core Client API + * %% + * Copyright (C) 2014 - 2017 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import com.google.common.collect.Lists; +import org.duniter.core.util.Preconditions; +import org.duniter.core.util.StringUtils; +import org.duniter.core.util.http.InetAddressUtils; + +import java.io.IOException; +import java.util.List; +import java.util.regex.Matcher; + +/** + * Created by blavenie on 07/12/16. + */ +public class NetworkPeerings { + + private NetworkPeerings() { + // helper class + } + + public static NetworkPeering parse(String document) throws IOException { + Preconditions.checkNotNull(document); + + try { + + NetworkPeering result = new NetworkPeering(); + + String[] lines = document.trim().split("\n"); + + Preconditions.checkArgument(lines.length >= 7, "Invalid document"); + + int i = 0; + String line; + for (; i < 5; ) { + line = lines[i++].trim(); + if (line.startsWith("Version: ")) { + result.setVersion(line.substring(9)); + } else if (line.startsWith("Type: ")) { + String type = line.substring(6); + Preconditions.checkArgument(Protocol.TYPE_PEER.equals(type), "Invalid type found in document. Expected: " + Protocol.TYPE_PEER); + } else if (line.startsWith("Currency: ")) { + result.setCurrency(line.substring(10)); + } else if (line.startsWith("PublicKey: ")) { + result.setPubkey(line.substring(11)); + } else if (line.startsWith("Block: ")) { + result.setBlock(line.substring(7)); + } + } + line = lines[i++].trim(); + Preconditions.checkArgument("Endpoints:".equals(line), "Invalid document format. Missing line 'Endpoint:' !"); + List<NetworkPeering.Endpoint> endpoints = Lists.newArrayList(); + for (; i < lines.length - 1; ) { + line = lines[i++].trim(); + NetworkPeering.Endpoint ep = Endpoints.parse(line); + Preconditions.checkNotNull(ep, "Unaparsable endpoint: " + line); + endpoints.add(ep); + } + result.setEndpoints(endpoints.toArray(new NetworkPeering.Endpoint[endpoints.size()])); + + result.setSignature(lines[lines.length - 1]); + + return result; + } + catch(Exception e) { + throw new IOException(e.getMessage()); + } + } + + public static void parseDefaultFormatEndPoint(Matcher matcher, NetworkPeering.Endpoint endpoint, int startGroup) { + for(int i=startGroup; i<=matcher.groupCount(); i++) { + String word = matcher.group(i); + + if (StringUtils.isNotBlank(word)) { + if (InetAddressUtils.isIPv4Address(word)) { + endpoint.ipv4 = word; + } else if (InetAddressUtils.isIPv6Address(word)) { + endpoint.ipv6 = word; + } else if (i == matcher.groupCount() || (i == matcher.groupCount() -1) && word.matches("\\d+")){ + endpoint.port = Integer.parseInt(word); + } else if (word.startsWith("/")) { + endpoint.path = word; + } else { + endpoint.dns = word; + } + } + } + } +} \ No newline at end of file diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerServiceImpl.java index 4b12796e3b87c515ebdae695cbcd1d7970c2bc77..46873230fc2b4df211e40ab2d93baa00715ea3fe 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerServiceImpl.java @@ -195,25 +195,27 @@ public class PeerServiceImpl implements PeerService, InitializingBean { public void save(String currencyId, List<Peer> peers, boolean isFullUpList) { int peerDownTimeoutMs = config.getPeerUpMaxAge(); - final long nowInSec = System.currentTimeMillis()/1000; + final long now = System.currentTimeMillis(); if (CollectionUtils.isNotEmpty(peers)) { if (log.isDebugEnabled()) { log.debug(String.format("[%s] Updating peers (%s endpoints found)", currencyId, peers.size())); } - // On each UP peers: set last UP time - peers.stream().map(Peer::getStats) - .filter(Peer.Stats::isReacheable) - .forEach(stats -> stats.setLastUpTime(nowInSec)); - - peers.forEach(this::save); + peers.forEach(peer -> { + // On each UP peers: set last UP time + if (peer.getStats() != null && peer.getStats().isReacheable()) { + peer.getStats().setLastUpTime(now / 1000); + } + // Save + save(peer); + }); } // Mark old peers as DOWN if (isFullUpList && peerDownTimeoutMs > 0) { - Date oldDate = new Date(nowInSec * 1000 - peerDownTimeoutMs); - peerDao.updatePeersAsDown(currencyId, oldDate.getTime() / 1000); + long maxUpTimeInMs = now - peerDownTimeoutMs; + peerDao.updatePeersAsDown(currencyId, maxUpTimeInMs / 1000); } } diff --git a/duniter4j-core-client/src/test/java/org/duniter/core/client/model/bma/EndpointsTest.java b/duniter4j-core-client/src/test/java/org/duniter/core/client/model/bma/EndpointsTest.java index d86ec9563618f36ed8afc3e4db06222e8189db54..5bebfa2e3895283849f55812e31565522c2fbe4e 100644 --- a/duniter4j-core-client/src/test/java/org/duniter/core/client/model/bma/EndpointsTest.java +++ b/duniter4j-core-client/src/test/java/org/duniter/core/client/model/bma/EndpointsTest.java @@ -8,10 +8,40 @@ public class EndpointsTest { @Test public void parse() throws Exception { - NetworkPeering.Endpoint ep = Endpoints.parse("GCHANGE_API data.gchange.fr 443"); + NetworkPeering.Endpoint ep = Endpoints.parse("BASIC_MERKLED_API g1.duniter.fr 81.81.81.81 80"); Assert.assertNotNull(ep); + Assert.assertEquals(EndpointApi.BASIC_MERKLED_API, ep.api); + Assert.assertEquals("g1.duniter.fr", ep.dns); + Assert.assertEquals("81.81.81.81", ep.ipv4); + Assert.assertNotNull(ep.port); + Assert.assertEquals(80, ep.port.intValue()); + Assert.assertNull(ep.id); + Assert.assertNull(ep.path); + ep = Endpoints.parse("BMAS g1.duniter.fr 443"); + Assert.assertNotNull(ep); + Assert.assertEquals(EndpointApi.BMAS, ep.api); + Assert.assertEquals("g1.duniter.fr", ep.dns); + Assert.assertNotNull(ep.port); + Assert.assertEquals(443, ep.port.intValue()); + Assert.assertNull(ep.id); + Assert.assertNull(ep.path); + + ep = Endpoints.parse("WS2P fb17fcd4 g1.duniter.fr 443 /ws2p"); + Assert.assertNotNull(ep); + Assert.assertNotNull(ep.id); + Assert.assertNotNull(ep.path); + Assert.assertEquals(EndpointApi.WS2P, ep.api); + Assert.assertEquals("g1.duniter.fr", ep.dns); + Assert.assertNotNull(ep.port); + Assert.assertEquals(443, ep.port.intValue()); + + ep = Endpoints.parse("GCHANGE_API data.gchange.fr 443"); + Assert.assertNotNull(ep); Assert.assertEquals(ep.api, EndpointApi.GCHANGE_API); + Assert.assertNull(ep.id); + Assert.assertNull(ep.path); + } } diff --git a/duniter4j-core-client/src/test/java/org/duniter/core/client/model/bma/NetworkPeeringsTest.java b/duniter4j-core-client/src/test/java/org/duniter/core/client/model/bma/NetworkPeeringsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c1217be30a3534e2be363031234add08c15a4d7a --- /dev/null +++ b/duniter4j-core-client/src/test/java/org/duniter/core/client/model/bma/NetworkPeeringsTest.java @@ -0,0 +1,24 @@ +package org.duniter.core.client.model.bma; + +import org.junit.Assert; +import org.junit.Test; + +public class NetworkPeeringsTest { + + @Test + public void parse() throws Exception { + + String doc = "Version: 10\nType: Peer\nCurrency: g1\nPublicKey: 38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE\nBlock: 162694-0000067CAF81B13E4BD7AE72A06F9981D80EB957E4D46C23A67B4DF734E258ED\nEndpoints:\nBMAS g1.duniter.fr 443\nES_CORE_API g1.data.duniter.fr 443\nES_USER_API g1.data.duniter.fr 443\nES_SUBSCRIPTION_API g1.data.duniter.fr 443\nBASIC_MERKLED_API g1.duniter.fr 80\nWS2P fb17fcd4 g1.duniter.fr 443 /ws2p\nU+obPZqDQ3WDDclyCrOhT80Dq/8sPZp0ng+hj4THPAaxKNQwc9cijNnfvwzSsQ/hZBJpZ6+Gzrzso+zprhNICQ==\n"; + + NetworkPeering peering = NetworkPeerings.parse(doc); + Assert.assertNotNull(peering); + + Assert.assertEquals("g1", peering.getCurrency()); + Assert.assertEquals("10", peering.getVersion()); + Assert.assertEquals("38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE", peering.getPubkey()); + Assert.assertEquals("162694-0000067CAF81B13E4BD7AE72A06F9981D80EB957E4D46C23A67B4DF734E258ED", peering.getBlock()); + Assert.assertEquals("U+obPZqDQ3WDDclyCrOhT80Dq/8sPZp0ng+hj4THPAaxKNQwc9cijNnfvwzSsQ/hZBJpZ6+Gzrzso+zprhNICQ==", peering.getSignature()); + Assert.assertEquals(6, peering.getEndpoints().length); + } + +}