Commit b01e13cb authored by Benoit Lavenier's avatar Benoit Lavenier

[fix] Fix endpoints parsing. Add unit tests

parent c23dd490
......@@ -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);
}
......@@ -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));
}
......
......@@ -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;
......
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
......@@ -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);
}
}
......
......@@ -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);
}
}
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);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment