better exception handling for runtime exceptions in the server handler
This commit is contained in:
parent
57af07c950
commit
453fc20615
5 changed files with 85 additions and 11 deletions
|
@ -1,6 +1,6 @@
|
|||
group = org.xbib
|
||||
name = netty-http
|
||||
version = 4.1.52.0
|
||||
version = 4.1.52.1
|
||||
|
||||
gradle.wrapper.version = 6.4.1
|
||||
netty.version = 4.1.52.Final
|
||||
|
|
|
@ -19,4 +19,6 @@ public interface Domain<R extends EndpointResolver<?>> {
|
|||
Collection<? extends X509Certificate> getCertificateChain();
|
||||
|
||||
void handle(ServerRequest.Builder serverRequestBuilder, ServerResponse.Builder serverResponseBuilder) throws IOException;
|
||||
|
||||
void handleAfterError(ServerRequest.Builder serverRequestBuilder, ServerResponse.Builder serverResponseBuilder, Throwable throwable);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.xbib.netty.http.server.endpoint.HttpEndpointResolver;
|
|||
import org.xbib.netty.http.server.api.Filter;
|
||||
import org.xbib.netty.http.server.security.CertificateUtils;
|
||||
import org.xbib.netty.http.server.security.PrivateKeyUtils;
|
||||
import org.xbib.netty.http.server.util.ExceptionFormatter;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.IOException;
|
||||
|
@ -40,12 +41,16 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* The {@code HttpServerDomain} class represents a virtual server with a name.
|
||||
*/
|
||||
public class HttpServerDomain implements Domain<HttpEndpointResolver> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(HttpServerDomain.class.getName());
|
||||
|
||||
private final String name;
|
||||
|
||||
private final HttpAddress httpAddress;
|
||||
|
@ -164,12 +169,20 @@ public class HttpServerDomain implements Domain<HttpEndpointResolver> {
|
|||
} else {
|
||||
if (serverResponseBuilder != null) {
|
||||
serverResponseBuilder.setStatus(HttpResponseStatus.NOT_FOUND)
|
||||
.setContentType("text/plain")
|
||||
.setContentType("text/plain;charset=utf-8")
|
||||
.build().write("no endpoint found to match request");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAfterError(ServerRequest.Builder serverRequestBuilder, ServerResponse.Builder serverResponseBuilder, Throwable throwable) {
|
||||
logger.log(Level.SEVERE, throwable.getMessage(), throwable);
|
||||
serverResponseBuilder.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR)
|
||||
.setContentType("text/plain;charset=utf-8")
|
||||
.build().write(ExceptionFormatter.format(throwable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + " (" + httpAddress + ")";
|
||||
|
@ -335,7 +348,7 @@ public class HttpServerDomain implements Domain<HttpEndpointResolver> {
|
|||
.addEndpoint(HttpEndpoint.builder()
|
||||
.setPath(path)
|
||||
.build())
|
||||
.setDispatcher(filter::handle)
|
||||
.setDispatcher(filter)
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
@ -349,7 +362,7 @@ public class HttpServerDomain implements Domain<HttpEndpointResolver> {
|
|||
.setPrefix(prefix)
|
||||
.setPath(path)
|
||||
.build())
|
||||
.setDispatcher(filter::handle)
|
||||
.setDispatcher(filter)
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
@ -365,7 +378,7 @@ public class HttpServerDomain implements Domain<HttpEndpointResolver> {
|
|||
.setPath(path)
|
||||
.setMethods(Arrays.asList(methods))
|
||||
.build())
|
||||
.setDispatcher(filter::handle)
|
||||
.setDispatcher(filter)
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -207,9 +207,9 @@ public final class Server implements AutoCloseable {
|
|||
}
|
||||
|
||||
public URL getBaseURL(HttpHeaders headers) {
|
||||
String scheme = null;
|
||||
String host = null;
|
||||
String port = null;
|
||||
String scheme;
|
||||
String host;
|
||||
String port;
|
||||
if (headers == null) {
|
||||
URL bindURL = serverConfig.getDefaultDomain().getHttpAddress().base();
|
||||
scheme = bindURL.getScheme();
|
||||
|
@ -258,7 +258,7 @@ public final class Server implements AutoCloseable {
|
|||
}
|
||||
|
||||
public void handle(ServerRequest.Builder serverRequestBuilder,
|
||||
ServerResponse.Builder serverResponseBuilder) throws IOException {
|
||||
ServerResponse.Builder serverResponseBuilder) {
|
||||
URL baseURL = getBaseURL(serverRequestBuilder.getHeaders());
|
||||
serverRequestBuilder.setBaseURL(baseURL);
|
||||
Domain<? extends EndpointResolver<?>> domain = getDomain(baseURL);
|
||||
|
@ -266,8 +266,9 @@ public final class Server implements AutoCloseable {
|
|||
executor.submit(() -> {
|
||||
try {
|
||||
domain.handle(serverRequestBuilder, serverResponseBuilder);
|
||||
} catch (IOException e) {
|
||||
executor.afterExecute(null, e);
|
||||
} catch (Throwable t) {
|
||||
executor.afterExecute(null, t);
|
||||
domain.handleAfterError(serverRequestBuilder, serverResponseBuilder, t);
|
||||
} finally {
|
||||
serverRequestBuilder.release();
|
||||
}
|
||||
|
@ -275,6 +276,8 @@ public final class Server implements AutoCloseable {
|
|||
} else {
|
||||
try {
|
||||
domain.handle(serverRequestBuilder, serverResponseBuilder);
|
||||
} catch (Throwable t) {
|
||||
domain.handleAfterError(serverRequestBuilder, serverResponseBuilder, t);
|
||||
} finally {
|
||||
serverRequestBuilder.release();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package org.xbib.netty.http.server.util;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* Format exception messages and stack traces.
|
||||
*/
|
||||
public final class ExceptionFormatter {
|
||||
|
||||
private ExceptionFormatter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Format exception with stack trace.
|
||||
*
|
||||
* @param t the thrown object
|
||||
* @return the formatted exception
|
||||
*/
|
||||
public static String format(Throwable t) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
append(sb, t, 0, true);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append Exception to string builder.
|
||||
*/
|
||||
private static void append(StringBuilder sb, Throwable t, int level, boolean details) {
|
||||
if (((t != null) && (t.getMessage() != null)) && (!t.getMessage().isEmpty())) {
|
||||
if (details && (level > 0)) {
|
||||
sb.append("\n\nCaused by\n");
|
||||
}
|
||||
sb.append(t.getMessage());
|
||||
}
|
||||
if (details) {
|
||||
if (t != null) {
|
||||
if ((t.getMessage() != null) && (t.getMessage().isEmpty())) {
|
||||
sb.append("\n\nCaused by ");
|
||||
} else {
|
||||
sb.append("\n\n");
|
||||
}
|
||||
}
|
||||
StringWriter sw = new StringWriter();
|
||||
if (t != null) {
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
}
|
||||
sb.append(sw.toString());
|
||||
}
|
||||
if (t != null) {
|
||||
if (t.getCause() != null) {
|
||||
append(sb, t.getCause(), level + 1, details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue