Embedded Jetty web server not working, handler not invoked - java

I'm trying to embed Jetty 8 (8.1.18.v20150929) into a Java (jdk1.7.0_67) application. I have the following code:
public static final String HTTP_PATH = "/session";
public static final int HTTP_PORT = 9995;
// Open the HTTP server for listening to requests.
logger.info("Starting HTTP server, Port: " + HTTP_PORT + ", Path: "
+ "/session");
httpServer = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(HTTP_PORT);
connector.setHost("localhost");
httpServer.addConnector(connector);
TestHttpHandler handler = new TestHttpHandler(this);
ContextHandler ch = new ContextHandler();
ch.setContextPath(HTTP_PATH);
ch.setHandler(handler);
httpServer.setHandler(ch);
try {
httpServer.start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
My handler is pretty basic as a test:
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
logger.debug("Handling");
}
If I run the app and then use CURL to send a GET request to http://localhost:9995/session, then it returns a 200 status but there's no debug output.
If I access http://localhost:9995/session2, I get a 404 error.
I've read many examples online but for some reason I can't seem to get the handler to work properly. Am I doing something wrong? Thanks

I had exactly the same problem, and this is just a misunderstanding about how the Jetty API works. I was expecting to use ContextHandlers as a minimal implementation of REST services, however ContextHandlers are meant to handle requests to an entire context base (for example http://server:80/context-base, i.e. the equivalent of an app in Tomcat). The correct way to solve this question is to use Servlets:
Server server = new Server(9995);
ServletContextHandler root = new ServletContextHandler(ServletContextHandler.NO_SECURITY | ServletContextHandler.NO_SESSIONS);
root.setContextPath("/");
ServletHolder holder = new ServletHolder(new HttpServlet() {
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
logger.debug("Handling");
}
});
server.start();
server.join();

Related

How to enable only http on some endpoints and https on some other endpoints in a spring boot rest controller?

I want to enable http on some endpoints and https on another set of endpoints.
I got solutions like configure https through application.properties and http by programmatically creating an extra connector, but all the results enable both http and https for all endpoints.
Can someone let me know how to configure some endpoints with https and some end points with http?
I figured this out for Jetty servlet which I use. If you use the default TomCat servlet you will have to do something similar that works for TomCat I suppose.
So to start with I have a ssl port as default that is activated. To also allow http you need to configure an additional http port in your config. Then you need to add a server Handler. You could add the Handler SecuredRedirectHandler to redirect ALL http requests to the https port. Since we don't want to redirect ALL http requests we make our own CustomRedirectHandler that extends SecuredRedirectHandler.
#Bean
public ConfigurableServletWebServerFactory webServerFactory() {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.addServerCustomizers(new JettyServerCustomizer() {
#Override
public void customize(Server server) {
final HttpConnectionFactory httpConnectionFactory = server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class);
// Enable HTTP for assigned port
final ServerConnector httpConnector = new ServerConnector(server, httpConnectionFactory);
httpConnector.setPort(serverProperties.intHttpPort() /* HTTP */);
server.addConnector(httpConnector);
// Add a CustomRedirectHandler to Server Handlers
final HandlerList handlerList = new HandlerList();
handlerList.addHandler(new CustomRedirectHandler());
for(Handler handler : server.getHandlers()) {
handlerList.addHandler(handler);
}
server.setHandler(handlerList);
}
});
return factory;
}
In our CustomRedirectHandler we can check if the requested endpoint is in our "allowed http" array. If it already request https or is allowed http then we do nothing, else redirect to https. My example allows http only for the endpoint that starts with "/.well-known/acme-challenge/" to allow requests to http://example.com/.well-known/acme-challenge/TOKEN for example.
public class CustomRedirectHandler extends SecuredRedirectHandler {
private final String[] allowedHttp = {"/.well-known/acme-challenge/"};
#Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
HttpChannel channel = baseRequest.getHttpChannel();
if (baseRequest.isSecure() || channel == null) {
// nothing to do, already requested https
return;
}
// Check if request is for allowed http
if (allowHttp(baseRequest)) {
return;
}
// Else Redirect to https
super.handle(target, baseRequest, request, response);
}
public boolean allowHttp(Request baseRequest) {
String pathInfo = baseRequest.getPathInfo();
if (pathInfo == null) {
return false;
}
for (String allowed : allowedHttp) {
if (pathInfo.startsWith(allowed)) {
return true;
}
}
return false;
}
}

Cannot find a handler for POST with boundary

I'm in the midst of testing my application which is using an HTTP-server. Instead of mocking I decided to go with a HTTP server fixture. Meaning that I do not have to mock any productional code. To accomplish this goal I currently chose for a free to use 3rd party library fixd.
I was able to successfully create several unit tests - which are working by means of a GET request. Most are quite simple, i.e.:
#Test
public void verifyConnectionTest()
{
try
{
final String body = FileUtils.readFileToString(RESOURCE);
final String path = "/";
this.server.handle(Method.GET, path).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
But I now have to send a POST request with multipart/form-data. Which does not make much of a difference other than changing the method and content-type:
#Test
public void executeStepTest()
{
try
{
final String body = FileUtils.readFileToString(SERVICE_RESPONSE);
final String path = "/";
this.server.handle(Method.POST, path, "multipart/form-data").with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
However I get the following error: [ERROR] could not find a handler for POST - / - multipart/form-data; boundary=bqCBI7t-VW1xaJW7BADmTiGMg9w_YM2sHH8ukJYx and my guess is that fixd doesn't recognize the boundary-party. Since the documentation does not show an example I'm quite stuck on this part.
I tried using some wildcards such as '*', no succes. Thus; I need a way to either tell fixd to accept that boundary or use some wildcards I didn't yet discover. Any help would be great, thanks!
I've been making some debug and it seems to be that the problem is in the fixd core.
Basically, fixd indexes every RequestHandlerImpl by a HandlerKey (which includes ContentType as part of the key) in the map handlerMap. See method org.bigtesting.fixd.core.FixtureContainer#resolve.
...
HandlerKey key = new HandlerKey(method, route, contentType);
RequestHandlerImpl handler = handlerMap.get(key);
if (handler == null) {
// Error
}
...
Problem: When the request is multipart/form-data, boundary data (which it's generated dinamically every request) is part of the content type. So, any handler is found in handlerMap because the key changes with every running.
I've made a little test only to check that this is the cause of the problem, passing the contentType to fixd server.handle after the creation of the multipart request, and it works fine.
See the test below:
#Test
public void verifyConnectionTest_multipart() {
try {
// 1. Create multipart request (example with http-commons 3.1)
PostMethod filePost = new PostMethod(url);
Part[] parts = { new StringPart("param", "value") };
MultipartRequestEntity request = new MultipartRequestEntity(parts, filePost.getParams());
filePost.setRequestEntity(request);
// 2. fixd server handle (passing the request content type)
this.server.handle(Method.POST, "/", request.getContentType()).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response) {
response.setStatusCode(200);
response.setContentType("text/xml");
}
});
// 3. Execute multipart request
HttpClient client = new HttpClient();
int status = client.executeMethod(filePost);
// 4. Assertions
Assert.assertEquals(200, status);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
Hope it helps you to clarify the problem. Cheers
This was a bug in fixd, and has been fixed in version 1.0.3. Your original code should work using this new version of fixd.

How to combine Jetty with embedding ServletContextHandler and AbstractHandler?

Is it possible to realize Jetty server by java that will listening to different ports and has two different handlers (ServletContextHandler/extends AbstractHandler)?
I have json request/response service that work on port 8899 (extends AbstractHandler) and webapp that work on port 8080 (ServletContextHandler) at one Jetty server.
Here is ServletContextHandler, listening 8080:
private static ServletContextHandler getServletContextHandler() throws IOException {
WebAppContext contextHandler = new WebAppContext();
contextHandler.setErrorHandler(null);
contextHandler.setContextPath(CONTEXT_PATH);
WebApplicationContext context = getContext();
contextHandler.addServlet(new ServletHolder(new DispatcherServlet(context)), CONTEXT_PATH);
contextHandler.addEventListener(new ContextLoaderListener(context));
contextHandler.setResourceBase(new ClassPathResource("webapp").getURI().toString());
contextHandler.setDescriptor("/webapp/WEB-INF/webstat.xml");
return contextHandler;
}
private static WebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("webapp/WEB-INF/");
log.info("CONTEXT name {} locations {}", context.getDisplayName(), context.getConfigLocations());
return context;
}
Handler that listening 8899:
private class Handler extends AbstractHandler {
public void handle(String target,
Request baseRequest,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
String contentEncoding = request.getHeader(RpcClient.CONTENT_ENCODING);
String contentType = request.getHeader(RpcClient.CONTENT_TYPE);
if ((contentEncoding != null && contentEncoding.startsWith(RpcClient.APPLICATION_JSON))||
contentType.equals(RpcClient.APPLICATION_JSON)) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
}
baseRequest.setHandled(true);
}
}
And finally method that realize Jetty server:
public void init() {
try {
Server httpServer = new Server();
ServerConnector rpcConnector = new ServerConnector(httpServer);
rpcConnector.setHost("localhost");
rpcConnector.setPort(8899);
rpcConnector.setIdleTimeout(30000);
ServerConnector httpConnector = new ServerConnector(httpServer);
httpConnector.setHost("localhost");
httpConnector.setPort(8080);
httpConnector.setIdleTimeout(30000);
httpServer.setConnectors(new Connector[] {rpcConnector, httpConnector});
HandlerList handlerList = new HandlerList();
handlerList.setHandlers(new Handler[] {new Handler() }, new ContextHandler(getServletContextHandler()) {});//CAN'T APPLY them in HandlerList
httpServer.setHandler(handlerList);
httpServer.setStopAtShutdown(true);
httpServer.start();
log.info("WebServer start ...");
httpServer.join();
} catch (Exception ex) {
log.error("Exception in http server. Exception: {}", ex.getMessage());
}
}
I've check ContextHandlerCollection, HandlerList, HandlerCollection & etc.
The goal is to combine two different services for work at one jetty server. Logic of there work is that user go to http://localhost:8080/getStatistics, spring mvc send json request to localhost:8899, application collect statistics, and response it by json to spring mvc and it'll generate jsp with statistics to user.
Is any idea how to combine those Handlers?

Jetty Redirect Based on Locale

I want to -based on the locale of the requesting client- redirect a URL, server side in Jetty.
i.e.
client makes a request for host:port/help/index.html ('help' being a webapp war)
server side I read the clients locale, e.g. 'GB' and redirect to a seperate webapp, e.g. *host:port/help_GB/index.html*
I thought this would be as simple as the server side code that runs my Jetty server:-
String i18nID = Locale.getDefault().getCountry();
RewriteHandler rewrite = new RewriteHandler();
rewrite.setRewriteRequestURI(true);
rewrite.setRewritePathInfo(false);
rewrite.setOriginalPathAttribute("requestedPath");
RedirectRegexRule r = new RedirectRegexRule();
r.setRegex("/help/(.*)");
r.setReplacement("/help_" + i18nID + "/$1");
rewrite.addRule(r);
server.setHandler(rewrite);
But this doesn't work, I get 404s for all 'host:port/*' addresses. I then noticed that I was getting the locale server side anyhow and I want it client side so I wrote my own handler:-
private class MyHandler extends RewriteHandler
{
#Override
public void handle(String target,
Request baseRequest,
HttpServletRequest request,
HttpServletResponse response)
{
try
{
String country = baseRequest.getLocale().getCountry();
String newTarget = target.replace("/help/", "/help_" + country + "/");
if (target.contains("/help/") /*TODO And not GB locale */)
{
response.sendRedirect(newTarget);
}
else
{
super.handle(target, baseRequest, request, response);
}
}
catch(Exception e)
{
/*DEBUG*/System.out.println(e.getClass() + ": " + e.getMessage());
e.printStackTrace();
}
}
}
...and used that instead of RewriteHandler. This accepts '/help/' requests, doesn't redirect, doesn't include some page elements and 404s every other URI not containing help.
Am I doing something wrong or using the rewrite/redirect handlers some way they're not supposed to be used?!
Redirecting to another webapp should maybe be done with a Filter like so

sending request to the newly created jetty server for testing purposes

I'm writing integration JUnit test. My task is to test whether the response of my local server is correct. The mentioned server takes as a GET parameter an address of page to be analysed (for example: localhost:8000/test?url=http://www.example.com).
To avoid being dependent on www.example.com I want to start for this particular test my own jetty server, which always serves the same content.
private static class MockPageHandler extends AbstractHandler {
public void handle(String target,Request baseRequest, HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
final String responseString = loadResource("index.html");
response.getWriter().write(responseString);
baseRequest.setHandled(true);
}
}
public void test() throws Exception {
final int PORT = 8080;
final Server server = new Server(PORT);
server.setHandler(new MockPageHandler());
server.start();
final ContentResponse response =
client.newRequest("http://localhost:8000/test?url=http://localhost:8080").send();
/* some assertions. */
server.stop();
server.join();
}
Every time I execute this test, the handle method in MockPageHandler is never invoked.
Do you have any suggestions why this not works?
P.S. When I remove server.stop() and in browser type http://localhost:8080 the proper page is shown.
Quick answer:
Remove the server.join() line. That line makes the junit thread wait until the server thread stops. Which is not needed for unit testing.
Long answer:
What we (the jetty developers) have learned about using jetty embedded servers with junit.
Use the #Before and #After annotations to start and stop the server if you have 1 test method, or some requirement that the server be pristine between test methods.
Example #Before / #After (Jetty 9.x):
public class MyTest
{
private Server server;
private URI serverUri;
#Before
public void startServer() throws Exception
{
this.server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(0); // let connector pick an unused port #
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
// Serve capture servlet
context.addServlet(new ServletHolder(new MyServlet()),"/my/*");
// Start Server
server.start();
String host = connector.getHost();
if (host == null)
{
host = "localhost";
}
int port = connector.getLocalPort();
this.serverUri = new URI(String.format("http://%s:%d/",host,port));
}
#After
public void stopServer()
{
try
{
server.stop();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
#Test
public void testMe()
{
// Issue request to server
URI requestUri = serverUri.resolve("/my/test");
// assert the response
}
}
This technique makes the server start on port 0, which is a magic number that tells the underlying stack to pick an empty port and start listening. The test case then asks the server what port number it is listening on and builds out the serverUri field to be appropropriate for this test run.
This technique works great, however, it will start/stop the server for each method.
Enter, the better technique, use the #BeforeClass and #AfterClass annotations to start/stop the server once for the entire test class, running all of the methods inside of the test class against this started server.
Example #BeforeClass / #AfterClass (Jetty 9.x):
public class MyTest
{
private static Server server;
private static URI serverUri;
#BeforeClass
public static void startServer() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(0); // let connector pick an unused port #
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
// Serve capture servlet
context.addServlet(new ServletHolder(new MyServlet()),"/my/*");
// Start Server
server.start();
String host = connector.getHost();
if (host == null)
{
host = "localhost";
}
int port = connector.getLocalPort();
serverUri = new URI(String.format("http://%s:%d/",host,port));
}
#AfterClass
public static void stopServer()
{
try
{
server.stop();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
#Test
public void testMe()
{
// Issue request to server
URI requestUri = serverUri.resolve("/my/test");
// assert the response
}
}
Doesn't look much different? Yes, the changes are subtle. #Before became #BeforeClass, #After became #AfterClass. The start/stop methods are now static. The server and serverUri fields are now static.
This technique is used where we have dozens of test methods that access the same server, and those requests do not alter the state in the server. This speeds up the test case execution by simply not recreating the server between each test method.
Give a try to "com.jayway.restassured" for your http test. too easy to write some test :
#Test
public void testNotGetAll() {
expect().
statusCode(404).
when().
get(baseUrl+"/games/");
}
this method call "http://mywebserver.local:8080/rest/games/" and verify that a 404 http status code is returned.
And this approach synchronised with a Jetty server (for example) started at pre-integration-test in the maven lifecycle, you match the perfect mix to process integration test !

Categories