diff --git a/pom.xml b/pom.xml
index b39a80d..da148e0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
4.0.0
com.sshtools
uhttpd
- 0.9.5-SNAPSHOT
+ 0.9.5
µTTPD - A very small HTTP/HTTPS server
11
diff --git a/src/main/java/com/sshtools/uhttpd/UHTTPD.java b/src/main/java/com/sshtools/uhttpd/UHTTPD.java
index ec1e7e7..9a7902c 100644
--- a/src/main/java/com/sshtools/uhttpd/UHTTPD.java
+++ b/src/main/java/com/sshtools/uhttpd/UHTTPD.java
@@ -90,6 +90,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -101,6 +102,7 @@
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -322,14 +324,13 @@ public final void run() {
do {
wireProtocol.transact();
times++;
- } while (times < rootContext.keepAliveMax);
+ } while (!closed && times < rootContext.keepAliveMax);
} catch (ClosedChannelException | EOFException e) {
LOG.log(Level.TRACE, "EOF.", e);
} catch(SocketTimeoutException ste) {
LOG.log(Level.TRACE, "Timeout.", ste);
} catch (Exception e) {
LOG.log(Level.ERROR, "Failed handling connection.", e);
- e.printStackTrace();// argh
}
}
} catch (Exception e) {
@@ -1107,17 +1108,20 @@ public static class Session {
}
public static Session get() {
+ return get(true).orElseThrow();
+ }
+
+ public static Optional get(boolean create) {
var session = current.get();
- if(session == null) {
+ if(session == null && create) {
session = new Session();
current.set(session);
var tx = Transaction.get();
if(tx.responded()) {
throw new IllegalStateException("A session cookie must be created, but the response has already been committed.");
}
- return session;
}
- return session;
+ return Optional.ofNullable(session);
}
@Override
@@ -1538,7 +1542,7 @@ String text() {
/**
* Select a {@link Handler} based on its {@link Transaction#path()}, i.e. URI.
- * If the URI matches, the handler will be executred.
+ * If the URI matches, the handler will be executed.
*
*/
public static final class RegularExpressionSelector implements HandlerSelector {
@@ -1552,7 +1556,13 @@ public RegularExpressionSelector(String regexp) {
@Override
public boolean matches(Transaction req) {
var path = req.path().toString();
- return pattern.matcher(path).matches();
+ var matcher = pattern.matcher(path);
+ if(matcher.matches()) {
+ for(int i = 1 ; i <= matcher.groupCount(); i++) {
+ req.matches.add(matcher.group(i));
+ }
+ }
+ return matcher.matches();
}
}
@@ -2209,6 +2219,11 @@ public final Optional queryString() {
return queryString;
}
+
+ public final Transaction redirect(Status status, Path location) {
+ return redirect(status, location.toString());
+ }
+
public final Transaction redirect(Status status, String location) {
checkNotResponded();
if (status != Status.MOVED_PERMANENTLY && status != Status.MOVED_TEMPORARILY)
@@ -4865,7 +4880,7 @@ private final static class RootContextImpl extends AbstractContext implements Ro
private final ServerSocket sslServerSocket;
private final String threadName;
private final boolean daemon;
- private final Set clients = Collections.synchronizedSet(new HashSet<>());
+ private final Set clients = new CopyOnWriteArraySet<>();
private Thread otherThread;
private Thread serverThread;
@@ -4985,9 +5000,9 @@ protected void onClose() {
if (sslServerSocket != null)
sslServerSocket.close();
} finally {
- for(var c : new HashSet<>(clients)) {
+ while(!clients.isEmpty()) {
try {
- c.close();
+ clients.iterator().next().close();
}
catch(Exception e) {}
}