Commit f19e54b7 authored by Iain Learmonth's avatar Iain Learmonth
Browse files

Adds support for Bridgestrap metrics

#40006
parent eadbf01d
Pipeline #9989 passed with stage
in 2 minutes and 30 seconds
/* Copyright 2019--2020 The Tor Project
* Copyright 2021 SR2 Communications Limited
* See LICENSE for licensing information */
package org.torproject.descriptor;
import org.torproject.descriptor.Descriptor;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
public interface BridgestrapStats extends Descriptor {
LocalDateTime bridgestrapStatsEnd();
Duration bridgestrapStatsIntervalLength();
int bridgestrapCachedRequests();
Optional<List<BridgestrapTestResult>> bridgestrapTests();
}
/* Copyright 2021 SR2 Communications Limited
* See LICENSE for licensing information */
package org.torproject.descriptor;
import java.util.Optional;
public interface BridgestrapTestResult {
boolean isReachable();
Optional<String> hashedFingerprint();
}
/* Copyright 2019--2020 The Tor Project
* Copyright 2021 SR2 Communications Limited
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
import org.torproject.descriptor.BridgestrapStats;
import org.torproject.descriptor.BridgestrapTestResult;
import org.torproject.descriptor.DescriptorParseException;
import java.io.File;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Scanner;
import java.util.Set;
public class BridgestrapStatsImpl extends DescriptorImpl
implements BridgestrapStats {
private static final Set<Key> exactlyOnce = EnumSet.of(
Key.BRIDGESTRAP_STATS_END);
private static final Set<Key> atMostOnce = EnumSet.of(
Key.BRIDGESTRAP_CACHED_REQUESTS);
BridgestrapStatsImpl(byte[] rawDescriptorBytes, int[] offsetAndLength,
File descriptorFile) throws DescriptorParseException {
super(rawDescriptorBytes, offsetAndLength, descriptorFile, false);
this.parseDescriptorBytes();
this.checkExactlyOnceKeys(exactlyOnce);
this.checkFirstKey(Key.BRIDGESTRAP_STATS_END);
this.checkAtMostOnceKeys(atMostOnce);
this.clearParsedKeys();
}
BridgestrapStatsImpl(byte[] rawDescriptorBytes, File descriptorFile)
throws DescriptorParseException {
this(rawDescriptorBytes, new int[]{0, rawDescriptorBytes.length},
descriptorFile);
}
private void parseDescriptorBytes() throws DescriptorParseException {
Scanner scanner = this.newScanner().useDelimiter(NL);
while (scanner.hasNext()) {
String line = scanner.next();
if (line.startsWith("@")) {
continue;
}
String[] parts = line.split("[ \t]+");
Key key = Key.get(parts[0]);
switch (key) {
case BRIDGESTRAP_STATS_END:
this.parseBridgestrapStatsEnd(line, parts);
break;
case BRIDGESTRAP_CACHED_REQUESTS:
this.parseBridgestrapCachedRequests(line, parts);
break;
case BRIDGESTRAP_TEST:
this.parseBridgestrapTest(line, parts);
break;
case INVALID:
default:
ParseHelper.parseKeyword(line, parts[0]);
if (this.unrecognizedLines == null) {
this.unrecognizedLines = new ArrayList<>();
}
this.unrecognizedLines.add(line);
}
}
}
private void parseBridgestrapStatsEnd(String line, String[] parts)
throws DescriptorParseException {
if (parts.length < 5 || parts[3].length() < 2 || !parts[3].startsWith("(")
|| !parts[4].equals("s)")) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
this.bridgestrapStatsEnd = ParseHelper.parseLocalDateTime(line, parts,
1, 2);
this.bridgestrapStatsIntervalLength = ParseHelper.parseDuration(line,
parts[3].substring(1));
}
private void parseBridgestrapCachedRequests(String line, String[] parts)
throws DescriptorParseException {
if (parts.length < 2) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
try {
this.bridgestrapCachedRequests = Integer.parseInt(parts[1]);
} catch (NumberFormatException e) {
throw new DescriptorParseException("Illegal number format '"
+ line + "'.");
}
}
private void parseBridgestrapTest(String line, String[] parts)
throws DescriptorParseException {
if (parts.length < 2) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
if (null == this.bridgestrapTests) {
this.bridgestrapTests = new ArrayList<>();
}
boolean isReachable = Boolean.parseBoolean(parts[1]);
String fingerprint = null;
if (parts.length >= 3) {
fingerprint = parts[2];
}
this.bridgestrapTests.add(
new BridgestrapTestResultImpl(isReachable,
fingerprint));
}
private LocalDateTime bridgestrapStatsEnd;
@Override
public LocalDateTime bridgestrapStatsEnd() {
return this.bridgestrapStatsEnd;
}
private Duration bridgestrapStatsIntervalLength;
@Override
public Duration bridgestrapStatsIntervalLength() {
return this.bridgestrapStatsIntervalLength;
}
private int bridgestrapCachedRequests;
@Override
public int bridgestrapCachedRequests() {
return this.bridgestrapCachedRequests;
}
private List<BridgestrapTestResult> bridgestrapTests;
@Override
public Optional<List<BridgestrapTestResult>> bridgestrapTests() {
return Optional.ofNullable(this.bridgestrapTests);
}
}
\ No newline at end of file
/* Copyright 2021 SR2 Communications Limited
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
import org.torproject.descriptor.BridgestrapTestResult;
import java.util.Optional;
public class BridgestrapTestResultImpl implements BridgestrapTestResult {
private final boolean isReachable;
private final String hashedFingerprint;
public BridgestrapTestResultImpl(boolean reachable,
String hashedFingerprint) {
this.isReachable = reachable;
this.hashedFingerprint = hashedFingerprint;
}
@Override
public boolean isReachable() {
return this.isReachable;
}
@Override
public Optional<String> hashedFingerprint() {
return Optional.ofNullable(this.hashedFingerprint);
}
}
......@@ -140,7 +140,12 @@ public class DescriptorParserImpl implements DescriptorParser {
|| firstLines.startsWith(Key.BRIDGEDB_METRICS_END.keyword + SP)
|| firstLines.contains(NL + Key.BRIDGEDB_METRICS_END.keyword + SP)) {
return this.parseOneOrMoreDescriptors(rawDescriptorBytes, sourceFile,
Key.BRIDGEDB_METRICS_END, BridgedbMetricsImpl.class);
Key.BRIDGEDB_METRICS_END, BridgedbMetricsImpl.class);
} else if (firstLines.startsWith("@type bridgestrap-stats 1.")
|| firstLines.startsWith(Key.BRIDGESTRAP_STATS_END.keyword + SP)
|| firstLines.contains(NL + Key.BRIDGESTRAP_STATS_END.keyword + SP)) {
return this.parseOneOrMoreDescriptors(rawDescriptorBytes, sourceFile,
Key.BRIDGESTRAP_STATS_END, BridgestrapStatsImpl.class);
} else if (firstLines.startsWith("@type bandwidth-file 1.")
|| firstLines.matches("(?s)[0-9]{10}\\n.*")) {
/* Identifying bandwidth files by a 10-digit timestamp in the first line
......
......@@ -23,6 +23,9 @@ public enum Key {
BRIDGEDB_METRICS_END("bridgedb-metrics-end"),
BRIDGEDB_METRICS_VERSION("bridgedb-metrics-version"),
BRIDGEDB_METRIC_COUNT("bridgedb-metric-count"),
BRIDGESTRAP_STATS_END("bridgestrap-stats-end"),
BRIDGESTRAP_CACHED_REQUESTS("bridgestrap-cached-requests"),
BRIDGESTRAP_TEST("bridgestrap-test"),
BRIDGE_DISTRIBUTION_REQUEST("bridge-distribution-request"),
BRIDGE_IPS("bridge-ips"),
BRIDGE_IP_TRANSPORTS("bridge-ip-transports"),
......
/* Copyright 2019--2020 The Tor Project
* Copyright 2021 SR2 Communications Limited
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.torproject.descriptor.BridgestrapStats;
import org.torproject.descriptor.BridgestrapTestResult;
import org.torproject.descriptor.DescriptorParseException;
import org.apache.commons.compress.utils.IOUtils;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Optional;
public class BridgestrapStatsImplTest {
@Test
public void testParse() throws IOException, DescriptorParseException {
URL resource = getClass().getClassLoader().getResource(
"bridgestrap/bridgestrap-collector");
assertNotNull(resource);
InputStream dataInputStream = resource.openStream();
assertNotNull(dataInputStream);
byte[] rawDescriptorBytes = IOUtils.toByteArray(dataInputStream);
BridgestrapStats bridgestrapStats = new BridgestrapStatsImpl(
rawDescriptorBytes, new int[]{0, rawDescriptorBytes.length},
null);
assertEquals(
LocalDateTime.of(
LocalDate.of(2021, 7, 20),
LocalTime.of(19, 11, 16)),
bridgestrapStats.bridgestrapStatsEnd());
assertEquals(5051,
bridgestrapStats.bridgestrapCachedRequests());
Optional<List<BridgestrapTestResult>> resultList =
bridgestrapStats.bridgestrapTests();
assertTrue(resultList.isPresent());
assertEquals(3281, resultList.get().size());
BridgestrapTestResult first = resultList.get().get(0);
assertFalse(first.isReachable());
assertTrue(first.hashedFingerprint().isPresent());
assertEquals(
"00795E6F05824B2DE96411735BF5D7A163CE63C2",
first.hashedFingerprint().get());
BridgestrapTestResult second = resultList.get().get(1);
assertFalse(second.isReachable());
assertTrue(second.hashedFingerprint().isPresent());
assertEquals(
"0089AD6FED844EA585DCC5127B6BD757C34F22C3",
second.hashedFingerprint().get());
BridgestrapTestResult last = resultList.get().get(
resultList.get().size() - 1);
assertTrue(last.isReachable());
assertTrue(last.hashedFingerprint().isPresent());
assertEquals(
"FFD817FAAA36F185F43474F74C446EA8D95EC38F",
last.hashedFingerprint().get());
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
Supports Markdown
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