Fix potential race condition when threads share a session

pull/32/head
Richard Körber 2017-04-14 12:03:50 +02:00
parent 08eaa61f75
commit 1f6d8aea0b
1 changed files with 23 additions and 20 deletions

View File

@ -22,6 +22,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Challenge;
@ -39,14 +40,14 @@ import org.shredzone.acme4j.util.JSON;
* volatile data. * volatile data.
*/ */
public class Session { public class Session {
private final Map<Resource, URI> resourceMap = new EnumMap<>(Resource.class); private final AtomicReference<Map<Resource, URI>> resourceMap = new AtomicReference<>();
private final AtomicReference<Metadata> metadata = new AtomicReference<>();
private final URI serverUri; private final URI serverUri;
private final AcmeProvider provider; private final AcmeProvider provider;
private KeyPair keyPair; private KeyPair keyPair;
private byte[] nonce; private byte[] nonce;
private JSON directoryJson; private JSON directoryJson;
private Metadata metadata;
private Locale locale = Locale.getDefault(); private Locale locale = Locale.getDefault();
protected Instant directoryCacheExpiry; protected Instant directoryCacheExpiry;
@ -183,7 +184,7 @@ public class Session {
*/ */
public URI resourceUri(Resource resource) throws AcmeException { public URI resourceUri(Resource resource) throws AcmeException {
readDirectory(); readDirectory();
return resourceMap.get(Objects.requireNonNull(resource, "resource")); return resourceMap.get().get(Objects.requireNonNull(resource, "resource"));
} }
/** /**
@ -194,7 +195,7 @@ public class Session {
*/ */
public Metadata getMetadata() throws AcmeException { public Metadata getMetadata() throws AcmeException {
readDirectory(); readDirectory();
return metadata; return metadata.get();
} }
/** /**
@ -204,26 +205,28 @@ public class Session {
private void readDirectory() throws AcmeException { private void readDirectory() throws AcmeException {
synchronized (this) { synchronized (this) {
Instant now = Instant.now(); Instant now = Instant.now();
if (directoryJson == null || !directoryCacheExpiry.isAfter(now)) { if (directoryJson != null && directoryCacheExpiry.isAfter(now)) {
directoryJson = provider().directory(this, getServerUri()); return;
directoryCacheExpiry = now.plus(Duration.ofHours(1)); }
directoryJson = provider().directory(this, getServerUri());
directoryCacheExpiry = now.plus(Duration.ofHours(1));
}
JSON meta = directoryJson.get("meta").asObject(); JSON meta = directoryJson.get("meta").asObject();
if (meta != null) { if (meta != null) {
metadata = new Metadata(meta); metadata.set(new Metadata(meta));
} else { } else {
metadata = new Metadata(JSON.empty()); metadata.set(new Metadata(JSON.empty()));
} }
resourceMap.clear(); Map<Resource, URI> map = new EnumMap<>(Resource.class);
for (Resource res : Resource.values()) { for (Resource res : Resource.values()) {
URI uri = directoryJson.get(res.path()).asURI(); URI uri = directoryJson.get(res.path()).asURI();
if (uri != null) { if (uri != null) {
resourceMap.put(res, uri); map.put(res, uri);
}
}
} }
} }
resourceMap.set(map);
} }
} }