diff --git a/rxnetty/src/main/java/io/reactivex/netty/protocol/http/server/DefaultErrorResponseGenerator.java b/rxnetty/src/main/java/io/reactivex/netty/protocol/http/server/DefaultErrorResponseGenerator.java index 967cad98..61943bd2 100644 --- a/rxnetty/src/main/java/io/reactivex/netty/protocol/http/server/DefaultErrorResponseGenerator.java +++ b/rxnetty/src/main/java/io/reactivex/netty/protocol/http/server/DefaultErrorResponseGenerator.java @@ -27,6 +27,8 @@ import java.io.PrintStream; import java.nio.charset.Charset; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author Nitesh Kant @@ -63,7 +65,7 @@ public void updateResponse(HttpServerResponse response, Throwable error) { printStream = new PrintStream(new ByteBufOutputStream(buffer)); error.printStackTrace(printStream); String errorPage = ERROR_HTML_TEMPLATE.replace(STACKTRACE_TEMPLATE_VARIABLE, - buffer.toString(Charset.defaultCharset())); + escapeHtml(buffer.toString(Charset.defaultCharset()))); response.writeString(errorPage); } finally { ReferenceCountUtil.release(buffer); @@ -78,6 +80,41 @@ public void updateResponse(HttpServerResponse response, Throwable error) { } } + private String escapeHtml(String src) { + Pattern compile = Pattern.compile("[<>\"&`{}']"); + Matcher matcher = compile.matcher(src); + StringBuffer buffer = new StringBuffer(); + while (matcher.find()) { + String rep = matcher.group(); + matcher.appendReplacement(buffer, escapeChar(rep)); + } + matcher.appendTail(buffer); + return buffer.toString(); + } + + private String escapeChar(String c) { + switch (c.charAt(0)) { + case '<': + return "<"; + case '>': + return ">"; + case '"': + return """; + case '&': + return "&"; + case '`': + return "`"; + case '{': + return "{"; + case '}': + return "}"; + case '\'': + return "'"; + default: + return c; + } + } + public static void main(String[] args) { RxNetty.createHttpServer(8888, new RequestHandler() { @Override diff --git a/rxnetty/src/test/java/io/reactivex/netty/protocol/http/server/DefaultErrorResponseGeneratorTest.java b/rxnetty/src/test/java/io/reactivex/netty/protocol/http/server/DefaultErrorResponseGeneratorTest.java new file mode 100644 index 00000000..f0f8ef6b --- /dev/null +++ b/rxnetty/src/test/java/io/reactivex/netty/protocol/http/server/DefaultErrorResponseGeneratorTest.java @@ -0,0 +1,70 @@ +package io.reactivex.netty.protocol.http.server; + +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.reactivex.netty.RxNetty; +import io.reactivex.netty.protocol.http.client.HttpClientRequest; +import io.reactivex.netty.protocol.http.client.HttpClientResponse; +import io.reactivex.netty.server.RxServer; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.junit.matchers.JUnitMatchers; +import rx.Observable; +import rx.functions.Func1; +import rx.functions.Func2; + +import java.nio.charset.Charset; + +public class DefaultErrorResponseGeneratorTest { + private RxServer, HttpServerResponse> server; + + @After + public void tearDown() throws Exception { + if (null != server) { + server.shutdown(); + } + } + + @Test + public void testErrorGenerator() throws Exception { + server = RxNetty.createHttpServer(0, new RequestHandler() { + @Override + public Observable handle( + HttpServerRequest request, + HttpServerResponse response) { + return Observable + .error(new IllegalStateException( + "I always throw an error<>'&\"{}.")); + } + }).start(); + + int port = server.getServerPort(); + + HttpClientRequest request = + HttpClientRequest.createGet("/"); + + String html = + RxNetty.createHttpClient("localhost", port).submit(request) + .flatMap(new Func1, Observable>() { + @Override + public Observable call(HttpClientResponse response) { + return response.getContent().map(new Func1() { + @Override + public String call(ByteBuf byteBuf) { + return byteBuf.toString(Charset.forName("ASCII")); + } + }).reduce(new Func2() { + @Override + public String call(String s, String s2) { + return s + s2; + } + }); + } + }).toBlocking().last(); + + Assert.assertThat("Unexpected response status", + html, + JUnitMatchers.containsString("I always throw an error<>'&"{}.\n")); + } +} \ No newline at end of file