Compare commits

...

2 Commits

Author SHA1 Message Date
Craig Raw
2a4475068f remove guava dependency 2024-11-28 10:57:18 +02:00
Craig Raw
6f15bc6d3c suppress jetty log initialization message 2024-11-28 09:43:20 +02:00
7 changed files with 94 additions and 69 deletions

View File

@ -4,7 +4,7 @@ plugins {
}
group = 'com.sparrowwallet'
version = '1.0.4'
version = '1.0.6'
def secrets = new Properties()
file("publish.properties").withInputStream {
@ -24,7 +24,6 @@ dependencies {
implementation('org.eclipse.jetty:jetty-client:9.4.54.v20240208')
implementation('io.reactivex.rxjava2:rxjava:2.2.15')
implementation('org.apache.commons:commons-lang3:3.7')
implementation('com.google.guava:guava:33.0.0-jre')
implementation('org.slf4j:slf4j-api:2.0.12')
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation('org.junit.jupiter:junit-jupiter')

View File

@ -0,0 +1,41 @@
package com.sparrowwallet.tern.http.client;
import java.util.Optional;
public class DefaultHttpProxySupplier implements IHttpProxySupplier {
private final String host;
private final int port;
private final HttpProxy httpProxy;
public DefaultHttpProxySupplier(String host, int port) {
this.host = host;
this.port = port;
this.httpProxy = computeHttpProxy(host, port);
}
private HttpProxy computeHttpProxy(String host, int port) {
if(host == null || host.isEmpty() || port < 0) {
return null;
}
return new HttpProxy(HttpProxyProtocol.SOCKS, host, port);
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
@Override
public Optional<HttpProxy> getHttpProxy(HttpUsage httpUsage) {
return Optional.ofNullable(httpProxy);
}
@Override
public void changeIdentity() {
//not implemented
}
}

View File

@ -1,6 +1,5 @@
package com.sparrowwallet.tern.http.client;
import com.google.common.net.HostAndPort;
import io.reactivex.Observable;
import java.util.Map;
@ -17,8 +16,8 @@ public class HttpClientService extends JettyHttpClientService {
this(requestTimeout, null);
}
public HttpClientService(HostAndPort torProxy) {
this(DEFAULT_REQUEST_TIMEOUT, new TorHttpProxySupplier(torProxy));
public HttpClientService(String host, int port) {
this(DEFAULT_REQUEST_TIMEOUT, new DefaultHttpProxySupplier(host, port));
}
public HttpClientService(IHttpProxySupplier httpProxySupplier) {
@ -41,20 +40,4 @@ public class HttpClientService extends JettyHttpClientService {
IHttpClient httpClient = getHttpClient(HttpUsage.DEFAULT);
return AsyncUtil.getInstance().blockingGet(httpClient.postString(url, headers, contentType, content)).get();
}
public HostAndPort getTorProxy() {
if(getHttpProxySupplier() instanceof TorHttpProxySupplier torHttpProxySupplier) {
return torHttpProxySupplier.getTorProxy();
}
return null;
}
public void setTorProxy(HostAndPort torProxy) {
if(getHttpProxySupplier() instanceof TorHttpProxySupplier torHttpProxySupplier) {
//Ensure all http clients are shutdown first
stop();
torHttpProxySupplier._setTorProxy(torProxy);
}
}
}

View File

@ -1,10 +1,10 @@
package com.sparrowwallet.tern.http.client;
import com.google.common.util.concurrent.RateLimiter;
import com.sparrowwallet.tern.http.client.socks5.Socks5Proxy;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.ProxyConfiguration;
import org.eclipse.jetty.client.Socks4Proxy;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
@ -23,7 +23,11 @@ public class JettyHttpClientService implements IHttpClientService {
public static final long DEFAULT_TIMEOUT = 30000;
// limit changing Tor identity on network error every 4 minutes
private static final double RATE_CHANGE_IDENTITY_ON_NETWORK_ERROR = 1.0 / 240;
private static final int RATE_CHANGE_IDENTITY_ON_NETWORK_ERROR = 4 * 60 * 1000;
static {
Log.getProperties().setProperty("org.eclipse.jetty.util.log.announce", "false");
}
protected Map<HttpUsage, JettyHttpClient> httpClients; // used by Sparrow
private final IHttpProxySupplier httpProxySupplier;
@ -91,7 +95,7 @@ public class JettyHttpClientService implements IHttpClientService {
}
protected Consumer<Exception> computeOnNetworkError() {
RateLimiter rateLimiter = RateLimiter.create(RATE_CHANGE_IDENTITY_ON_NETWORK_ERROR);
RateLimiter rateLimiter = new RateLimiter(1, RATE_CHANGE_IDENTITY_ON_NETWORK_ERROR);
return e -> {
if(!rateLimiter.tryAcquire()) {
if(log.isDebugEnabled()) {

View File

@ -0,0 +1,43 @@
package com.sparrowwallet.tern.http.client;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
public class RateLimiter {
private final int maxTokens;
private final long refillIntervalMillis;
private final AtomicInteger tokens;
private long lastRefillTimestamp;
private final ReentrantLock lock = new ReentrantLock();
public RateLimiter(int maxTokens, long refillIntervalMillis) {
this.maxTokens = maxTokens;
this.refillIntervalMillis = refillIntervalMillis;
this.tokens = new AtomicInteger(maxTokens);
this.lastRefillTimestamp = System.currentTimeMillis();
}
public boolean tryAcquire() {
refill();
if (tokens.get() > 0) {
tokens.decrementAndGet();
return true;
}
return false;
}
private void refill() {
lock.lock();
try {
long now = System.currentTimeMillis();
long elapsed = now - lastRefillTimestamp;
if (elapsed > refillIntervalMillis) {
int tokensToAdd = (int) (elapsed / refillIntervalMillis);
tokens.set(Math.min(maxTokens, tokens.get() + tokensToAdd));
lastRefillTimestamp = now;
}
} finally {
lock.unlock();
}
}
}

View File

@ -1,44 +0,0 @@
package com.sparrowwallet.tern.http.client;
import com.google.common.net.HostAndPort;
import java.util.Optional;
public class TorHttpProxySupplier implements IHttpProxySupplier {
private HostAndPort torProxy;
private HttpProxy httpProxy;
public TorHttpProxySupplier(HostAndPort torProxy) {
this.torProxy = torProxy;
this.httpProxy = computeHttpProxy(torProxy);
}
private HttpProxy computeHttpProxy(HostAndPort hostAndPort) {
if (hostAndPort == null) {
return null;
}
return new HttpProxy(HttpProxyProtocol.SOCKS, hostAndPort.getHost(), hostAndPort.getPort());
}
public HostAndPort getTorProxy() {
return torProxy;
}
// shouldnt call directly but use httpClientService.setTorProxy()
public void _setTorProxy(HostAndPort hostAndPort) {
// set proxy
this.torProxy = hostAndPort;
this.httpProxy = computeHttpProxy(hostAndPort);
}
@Override
public Optional<HttpProxy> getHttpProxy(HttpUsage httpUsage) {
return Optional.ofNullable(httpProxy);
}
@Override
public void changeIdentity() {
//unimplemented
}
}

View File

@ -1,6 +1,5 @@
module com.sparrowwallet.tern {
requires java.management;
requires com.google.common;
requires io.reactivex.rxjava2;
requires com.fasterxml.jackson.databind;
requires org.eclipse.jetty.io;