mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Add bootstrap percentage and HS desc uploads to observer interface.
This commit is contained in:
@@ -55,10 +55,10 @@ public class AndroidTorWrapper extends AbstractTorWrapper {
|
||||
* @param ioExecutor The wrapper will use this executor to run IO tasks,
|
||||
* some of which may run for the lifetime of the wrapper, so the executor
|
||||
* should have an unlimited thread pool.
|
||||
* @param eventExecutor The wrapper will use this executor to call
|
||||
* {@link StateObserver#observeState(TorState)}. To ensure that state
|
||||
* changes are observed in the order they occur, this executor should have
|
||||
* a single thread (eg the app's main thread).
|
||||
* @param eventExecutor The wrapper will use this executor to call the
|
||||
* {@link Observer observer} (if any). To ensure that events are observed
|
||||
* in the order they occur, this executor should have a single thread (eg
|
||||
* the app's main thread).
|
||||
* @param architecture The processor architecture of the Tor and pluggable
|
||||
* transport binaries.
|
||||
* @param torDirectory The directory where the Tor process should keep its
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType;
|
||||
import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper;
|
||||
import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.HiddenServiceProperties;
|
||||
import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.Observer;
|
||||
import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.TorState;
|
||||
import org.briarproject.nullsafety.InterfaceNotNullByDefault;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
@@ -148,10 +149,22 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
// Don't execute more than one connection status check at a time
|
||||
connectionStatusExecutor =
|
||||
new PoliteExecutor("TorPlugin", ioExecutor, 1);
|
||||
tor.setStateObserver(torState -> {
|
||||
State s = state.getState(torState);
|
||||
if (s == ACTIVE) backoff.reset();
|
||||
callback.pluginStateChanged(s);
|
||||
tor.setObserver(new Observer() {
|
||||
|
||||
@Override
|
||||
public void onState(TorState torState) {
|
||||
State s = state.getState(torState);
|
||||
if (s == ACTIVE) backoff.reset();
|
||||
callback.pluginStateChanged(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootstrapPercentage(int percentage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHsDescriptorUpload(String onion) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
@@ -61,6 +63,8 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
private static final String OWNER = "__OwningControllerProcess";
|
||||
private static final int COOKIE_TIMEOUT_MS = 3000;
|
||||
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
||||
private static final Pattern BOOTSTRAP_PERCENTAGE =
|
||||
Pattern.compile("PROGRESS=(\\d{1,3})");
|
||||
|
||||
protected final Executor ioExecutor;
|
||||
protected final Executor eventExecutor;
|
||||
@@ -112,8 +116,8 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStateObserver(@Nullable StateObserver stateObserver) {
|
||||
state.setObserver(stateObserver);
|
||||
public void setObserver(@Nullable Observer observer) {
|
||||
state.setObserver(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,9 +176,10 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
controlConnection.setEvents(asList(EVENTS));
|
||||
// Check whether Tor has already bootstrapped
|
||||
String info = controlConnection.getInfo("status/bootstrap-phase");
|
||||
if (info != null && info.contains("PROGRESS=100")) {
|
||||
LOG.info("Tor has already bootstrapped");
|
||||
state.setBootstrapped();
|
||||
if (info != null && info.contains("PROGRESS=")) {
|
||||
int percentage = parseBootstrapPercentage(info);
|
||||
if (percentage == 100) LOG.info("Tor has already bootstrapped");
|
||||
state.setBootstrapPercentage(percentage);
|
||||
}
|
||||
// Check whether Tor has already built a circuit
|
||||
info = controlConnection.getInfo("status/circuit-established");
|
||||
@@ -410,7 +415,9 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
if (parts.length < 2) {
|
||||
LOG.warning("Failed to parse HS_DESC UPLOADED event");
|
||||
} else if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("V3 descriptor uploaded for " + scrubOnion(parts[1]));
|
||||
String onion = parts[1];
|
||||
LOG.info("V3 descriptor uploaded for " + scrubOnion(onion));
|
||||
state.onHsDescriptorUploaded(onion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,9 +427,10 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
}
|
||||
|
||||
private void handleClientStatus(String msg) {
|
||||
if (msg.startsWith("BOOTSTRAP PROGRESS=100")) {
|
||||
LOG.info("Bootstrapped");
|
||||
state.setBootstrapped();
|
||||
if (msg.startsWith("BOOTSTRAP PROGRESS=")) {
|
||||
int percentage = parseBootstrapPercentage(msg);
|
||||
if (percentage == 100) LOG.info("Bootstrapped");
|
||||
state.setBootstrapPercentage(percentage);
|
||||
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
|
||||
if (state.setCircuitBuilt(true)) LOG.info("Circuit built");
|
||||
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
|
||||
@@ -435,6 +443,21 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
private int parseBootstrapPercentage(String s) {
|
||||
Matcher matcher = BOOTSTRAP_PERCENTAGE.matcher(s);
|
||||
if (matcher.matches()) {
|
||||
try {
|
||||
return Integer.parseInt(matcher.group(1));
|
||||
} catch (NumberFormatException e) {
|
||||
// Fall through
|
||||
}
|
||||
}
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Failed to parse bootstrap percentage: " + s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void handleGeneralStatus(String msg) {
|
||||
if (msg.startsWith("CLOCK_JUMPED")) {
|
||||
Long time = parseLongArgument(msg, "TIME");
|
||||
@@ -512,7 +535,7 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
|
||||
@GuardedBy("this")
|
||||
@Nullable
|
||||
private StateObserver observer = null;
|
||||
private Observer observer = null;
|
||||
|
||||
@GuardedBy("this")
|
||||
private boolean started = false,
|
||||
@@ -521,9 +544,14 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
networkEnabled = false,
|
||||
paddingEnabled = false,
|
||||
ipv6Enabled = false,
|
||||
bootstrapped = false,
|
||||
circuitBuilt = false;
|
||||
|
||||
@GuardedBy("this")
|
||||
private int bootstrapPercentage = 0;
|
||||
|
||||
@GuardedBy("this")
|
||||
private List<String> bridges = emptyList();
|
||||
|
||||
@GuardedBy("this")
|
||||
private int orConnectionsConnected = 0;
|
||||
|
||||
@@ -532,32 +560,25 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
private TorState state = null;
|
||||
|
||||
private synchronized void setObserver(
|
||||
@Nullable StateObserver observer) {
|
||||
@Nullable Observer observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void updateObserver() {
|
||||
private void updateState() {
|
||||
TorState newState = getState();
|
||||
if (newState != state) {
|
||||
state = newState;
|
||||
// Post the new state to the event executor. The contract of
|
||||
// this executor is to execute tasks in the order they're
|
||||
// submitted, so state changes will be observed in the correct
|
||||
// order but outside the lock
|
||||
if (observer != null) {
|
||||
eventExecutor.execute(() ->
|
||||
observer.observeState(newState));
|
||||
// Notify the observer on the event executor
|
||||
eventExecutor.execute(() -> observer.onState(newState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private List<String> bridges = emptyList();
|
||||
|
||||
private synchronized void setStarted() {
|
||||
started = true;
|
||||
updateObserver();
|
||||
updateState();
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
@@ -567,13 +588,18 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
|
||||
private synchronized void setStopped() {
|
||||
stopped = true;
|
||||
updateObserver();
|
||||
updateState();
|
||||
}
|
||||
|
||||
private synchronized void setBootstrapped() {
|
||||
if (bootstrapped) return;
|
||||
bootstrapped = true;
|
||||
updateObserver();
|
||||
private synchronized void setBootstrapPercentage(int percentage) {
|
||||
if (percentage == bootstrapPercentage) return;
|
||||
bootstrapPercentage = percentage;
|
||||
if (observer != null) {
|
||||
// Notify the observer on the event executor
|
||||
eventExecutor.execute(() ->
|
||||
observer.onBootstrapPercentage(percentage));
|
||||
}
|
||||
updateState();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -583,7 +609,7 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
private synchronized boolean setCircuitBuilt(boolean built) {
|
||||
if (built == circuitBuilt) return false; // Unchanged
|
||||
circuitBuilt = built;
|
||||
updateObserver();
|
||||
updateState();
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
@@ -598,7 +624,7 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
networkEnabled = enable;
|
||||
if (!enable) circuitBuilt = false;
|
||||
if (!wasInitialised || enable != wasEnabled) {
|
||||
updateObserver();
|
||||
updateState();
|
||||
}
|
||||
return enable != wasEnabled;
|
||||
}
|
||||
@@ -639,15 +665,15 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
if (!started || stopped) return STARTING_STOPPING;
|
||||
if (!networkInitialised) return CONNECTING;
|
||||
if (!networkEnabled) return DISABLED;
|
||||
return bootstrapped && circuitBuilt && orConnectionsConnected > 0
|
||||
? CONNECTED : CONNECTING;
|
||||
return bootstrapPercentage == 100 && circuitBuilt
|
||||
&& orConnectionsConnected > 0 ? CONNECTED : CONNECTING;
|
||||
}
|
||||
|
||||
private synchronized void onOrConnectionConnected() {
|
||||
int oldConnected = orConnectionsConnected;
|
||||
orConnectionsConnected++;
|
||||
logOrConnections();
|
||||
if (oldConnected == 0) updateObserver();
|
||||
if (oldConnected == 0) updateState();
|
||||
}
|
||||
|
||||
private synchronized void onOrConnectionClosed() {
|
||||
@@ -659,7 +685,7 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
}
|
||||
logOrConnections();
|
||||
if (orConnectionsConnected == 0 && oldConnected != 0) {
|
||||
updateObserver();
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,5 +695,13 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
|
||||
LOG.info(orConnectionsConnected + " OR connections connected");
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void onHsDescriptorUploaded(String onion) {
|
||||
if (observer != null) {
|
||||
// Notify the observer on the event executor
|
||||
eventExecutor.execute(() ->
|
||||
observer.onHsDescriptorUpload(onion));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public interface TorWrapper {
|
||||
* existing observer, or removes any existing observer if the argument is
|
||||
* null.
|
||||
*/
|
||||
void setStateObserver(@Nullable StateObserver stateObserver);
|
||||
void setObserver(@Nullable Observer observer);
|
||||
|
||||
/**
|
||||
* Returns the current state of the wrapper.
|
||||
@@ -129,16 +129,25 @@ public interface TorWrapper {
|
||||
|
||||
/**
|
||||
* An interface for observing changes to the {@link TorState state} of the
|
||||
* Tor process.
|
||||
* Tor process. All calls happen on the event executor supplied to the
|
||||
* wrapper's constructor.
|
||||
*/
|
||||
interface StateObserver {
|
||||
interface Observer {
|
||||
|
||||
/**
|
||||
* This method is called whenever the state of the Tor process changes.
|
||||
* The call happens on the event executor supplied to the wrapper's
|
||||
* constructor.
|
||||
* Called whenever the state of the Tor process changes.
|
||||
*/
|
||||
void observeState(TorState s);
|
||||
void onState(TorState s);
|
||||
|
||||
/**
|
||||
* Called whenever the bootstrap percentage changes.
|
||||
*/
|
||||
void onBootstrapPercentage(int percentage);
|
||||
|
||||
/**
|
||||
* Called whenever a hidden service descriptor is uploaded.
|
||||
*/
|
||||
void onHsDescriptorUpload(String onion);
|
||||
}
|
||||
|
||||
class HiddenServiceProperties {
|
||||
|
||||
@@ -18,10 +18,10 @@ public class UnixTorWrapper extends JavaTorWrapper {
|
||||
* @param ioExecutor The wrapper will use this executor to run IO tasks,
|
||||
* some of which may run for the lifetime of the wrapper, so the executor
|
||||
* should have an unlimited thread pool.
|
||||
* @param eventExecutor The wrapper will use this executor to call
|
||||
* {@link StateObserver#observeState(TorState)}. To ensure that state
|
||||
* changes are observed in the order they occur, this executor should have
|
||||
* a single thread (eg the app's main thread).
|
||||
* @param eventExecutor The wrapper will use this executor to call the
|
||||
* {@link Observer observer} (if any). To ensure that events are observed
|
||||
* in the order they occur, this executor should have a single thread (eg
|
||||
* the app's main thread).
|
||||
* @param architecture The processor architecture of the Tor and pluggable
|
||||
* transport binaries.
|
||||
* @param torDirectory The directory where the Tor process should keep its
|
||||
|
||||
@@ -24,9 +24,10 @@ public class WindowsTorWrapper extends JavaTorWrapper {
|
||||
* some of which may run for the lifetime of the wrapper, so the executor
|
||||
* should have an unlimited thread pool.
|
||||
* @param eventExecutor The wrapper will use this executor to call
|
||||
* {@link StateObserver#observeState(TorState)}. To ensure that state
|
||||
* changes are observed in the order they occur, this executor should have
|
||||
* a single thread (eg the app's main thread).
|
||||
* @param eventExecutor The wrapper will use this executor to call the
|
||||
* {@link Observer observer} (if any). To ensure that events are observed
|
||||
* in the order they occur, this executor should have a single thread (eg
|
||||
* the app's main thread).
|
||||
* @param architecture The processor architecture of the Tor and pluggable
|
||||
* transport binaries.
|
||||
* @param torDirectory The directory where the Tor process should keep its
|
||||
|
||||
Reference in New Issue
Block a user