Can I respond to POST requests using Jetty's ResourceHandler? - jetty

Can I respond to POST requests using Jetty's ResourceHandler? If so, how?
For context, here's snippet configuring a file server using ResourceHandler from the Jetty tutorials:
public class FileServer
{
public static void main(String[] args) throws Exception
{
Server server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(8080);
server.addConnector(connector);
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setDirectoriesListed(true);
resource_handler.setWelcomeFiles(new String[]{ "index.html" });
resource_handler.setResourceBase(".");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() });
server.setHandler(handlers);
server.start();
server.join();
}
}

The ResourceHandler seems to only support GET request. This makes sense, as the ResourceHandler only serves static resources (files, directories). A POST input would be discarded anyway.
I find it hard to make up a scenario, where one would need the ResourceHandler to reply to POST requests, but if you really want to achieve this, you could write your own Handler that wraps around the ResourceHandler and calls the GET methods for POST Requests. Some hints on how to do this can be found here: http://www.eclipse.org/jetty/documentation/current/writing-custom-handlers.html#passing-request-and-response

Related

HowTo configure the ErrorPageErrorHandler in embedded Jetty?

Is it possible to configure the ErrorPageErrorHandler in way that it redirects to a static Page if no content/service is found?
Here is my Code:
server = new Server(port);
Resource webRoot = Resource.newResource(webContent);
if (!webRoot.exists()) {
logger.warn("Unable to find root resource:" + webRoot.getName());
} else {
logger.info("Root resource is " + webRoot.getName());
}
ResourceHandler res = new ResourceHandler();
res.setBaseResource(webRoot);
res.setDirAllowed(false);
//servlet handler
ServletContextHandler servletCtx = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletCtx.setContextPath("/service");
servletCtx.addServlet(new ServletHolder("sample", new MyServletSample()), "/sample");
ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler();
errorHandler.addErrorPage(404, "index.html");
servletCtx.setErrorHandler(errorHandler);
// static file handler
ContextHandler staticCtx = new ContextHandler("/");
staticCtx.setBaseResource(webRoot);
staticCtx.setHandler(res);
// add handlers
HandlerList handlerList = new HandlerList();
handlerList.addHandler(servletCtx);
handlerList.addHandler(staticCtx);
// add handerList to server
server.setHandler(handlerList);
This code show me index.html on localhost:8080 and I can access the sample service http://localhost:8080/service/sample. However, I want to show a static error page (i.e. documentation) to show up if an error like "404 Not Found" occured.
With this code, the Error handler logs:
"WARN o.e.j.server.handler.ErrorHandler - No error page found
index.html"
. What is correct way/syntax to define the URI?
Thanks in advance!
This was answered before at https://stackoverflow.com/a/32383973/775715
Don't mix ResourceHandler and ServletContextHandler unless you REALLY know what you are doing, and fully understand the nature of javax.servlet.ServletContext and all of the rules it brings to the table.
See also:
What is difference between ServletContextHandler.setResourceBase and ResourceHandler.setResourceBase when using Jetty embedded container?
Serving static files from alternate path in embedded Jetty
Here's an example of your setup working with NO ResourceHandler, 1 ServletContextHandler, and a DefaultServlet providing the static file serving.
// servlet handler
ServletContextHandler servletCtx = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletCtx.setContextPath("/");
servletCtx.setBaseResource(webRoot); // what static content to serve
servletCtx.setWelcomeFiles(new String[] { "index.html" });
servletCtx.addServlet(new ServletHolder("sample", new MyServletSample()), "/service/sample");
ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler();
errorHandler.addErrorPage(404, "/index.html");
servletCtx.setErrorHandler(errorHandler);
// static file serving, and context based error handling
ServletHolder defaultServ = new ServletHolder("default", DefaultServlet.class);
defaultServ.setInitParameter("dirAllowed","false");
servletCtx.addServlet(defaultServ,"/");
// add handlers
HandlerList handlerList = new HandlerList();
handlerList.addHandler(servletCtx);
handlerList.addHandler(new DefaultHandler()); // non-context error handling

jetty - create main and resource servlet

I'm trying to create two servlets. first, the main servlet with the "/" path and a resource servlet from another path. but both paths starts from "/" (my work dir)
I wrote:
Server server = new Server(8001);
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(Servlet.class, "/");
ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setResourceBase("./classes/static/");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resourceHandler,handler});
server.setHandler(handlers);
server.start();
server.join();
but one handler overlap the other.
basically i want my code to act like :
handler.addServletWithMapping(Servlet.class, "/q");
(localhost:8001/q)
instead of:
handler.addServletWithMapping(Servlet.class, "/");
hope I was clear enough.
Thanks,
find it here
https://examples.javacodegeeks.com/enterprise-java/jetty/jetty-resource-handler-example/
needed to declare path to the contant :
Server server = new Server(8001);
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(Servlet.class, "/");
ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setResourceBase("./classes/static/");
resourceHandler.setDirectoriesListed(true);
ContextHandler contextHandler= new ContextHandler("/static");
contextHandler.setHandler(resourceHandler);
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] {contextHandler,handler});
server.setHandler(handlers);
server.start();
server.join();

jetty java web start standalone

I have (jetty) java web start standalone code something like this. I need to add SSL Certificate for the same.
org.mortbay.util.Log.instance().add(new TomeLogSink(NAME));
server = new HttpServer();
SocketListener listener = new SocketListener();
listener.setPort(port);
server.addListener(listener);
// context to handle all other request including servlets
HttpContext context = new HttpContext();
context.setContextPath(HttpConstants.HTTP_CONTEXT_PATH);
context.setResourceBase(HttpConstants.HTTP_PROVIDENT_ROOT);
//handler for servlets
servletHandler = new ServletHandler();
addServlet("*.jnlp", JnlpServlet.class);
addServlet("/index.htm", IndexServlet.class);
addServlet("/index.html", IndexServlet.class);
CcAdminServiceImpl cc = (CcAdminServiceImpl) RegistryManager.get(
CcAdminService.REGISTERED_NAME);
cc.createWebContent(this);
//handler for static content
ResourceHandler handler = new ResourceHandler();
handler.setDirAllowed(false);
handler.setAcceptRanges(false);
context.addHandler(servletHandler);
context.addHandler(handler);
context.addHandler(new NotFoundHandler());
//setup and start the server
server.addContext(createStaticContext(
HttpConstants.HTTP_HELP_PATH,
HttpConstants.HTTP_HELP_ROOT));
server.addContext(context);
server.start();

Cross Origin Filter with embedded Jetty

I'm trying to get a CrossOriginFilter working with a couple of embedded Jetty servers, both running on our internal network. Both are running servlets, but I need server A's web page to be able to post to server B's servlets. I think I need to add ACCESS_CONTROL_ALLOW_ORIGIN to a CrossOriginFilter but finding out how to do this with an embedded Jetty instance with no web.xml isn't proving to be easy. I get the following error message in the browser when trying to access server b's serlvets
No 'Access-Control-Allow-Origin' header is present on the requested resource
Im using angularjs to post to the other server's servlets in a controller.
And here is the code for one of the servers (both are pretty much the same)
Server server = new Server(httpPort);
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setDirectoriesListed(true);
resource_handler.setWelcomeFiles(new String[] { "index.html" });
resource_handler.setResourceBase("./http/");
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(ServerPageRoot.class, "/servlet/*");
FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin");
handler.addFilter(holder );
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, handler,new DefaultHandler() });
server.setHandler(handlers);
server.start();
A few points:
Don't use ServletHandler naked like that. The ServletHandler is an internal class that ServletContextHandler uses.
The ServletContextHandler is what provides the needed ServletContext object and state for the various servlets and filters you are using.
The ServletContextHandler also provides a place for the overall Context Path declaration
The ServletContextHandler is also the place for Welcome Files declaration.
Don't use ResourceHandler, when you have DefaultServlet available, its far more capable and feature rich.
Example:
Server server = new Server(httpPort);
// Setup the context for servlets
ServletContextHandler context = new ServletContextHandler();
// Set the context for all filters and servlets
// Required for the internal servlet & filter ServletContext to be sane
context.setContextPath("/");
// The servlet context is what holds the welcome list
// (not the ResourceHandler or DefaultServlet)
context.setWelcomeFiles(new String[] { "index.html" });
// Add a servlet
context.addServlet(ServerPageRoot.class,"/servlet/*");
// Add the filter, and then use the provided FilterHolder to configure it
FilterHolder cors = context.addFilter(CrossOriginFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin");
// Use a DefaultServlet to serve static files.
// Alternate Holder technique, prepare then add.
// DefaultServlet should be named 'default'
ServletHolder def = new ServletHolder("default", DefaultServlet.class);
def.setInitParameter("resourceBase","./http/");
def.setInitParameter("dirAllowed","false");
context.addServlet(def,"/");
// Create the server level handler list.
HandlerList handlers = new HandlerList();
// Make sure DefaultHandler is last (for error handling reasons)
handlers.setHandlers(new Handler[] { context, new DefaultHandler() });
server.setHandler(handlers);
server.start();
managed to get it working by doing
FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin");
holder.setName("cross-origin");
FilterMapping fm = new FilterMapping();
fm.setFilterName("cross-origin");
fm.setPathSpec("*");
handler.addFilter(holder, fm );
Maybe this will help someone even though it is not a good answer to the original question. I realized that you can easaly enable cross origin request sharing in an embedded jetty instance by manipulating the headers directly in your handler. The response object below is an instance of HttpServletResponse (which is passed to the handler).
Example:
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Methods", "POST, GET");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
I tried all the way of above answers and other similar ones. But always, I came across same error message.
Finally I reach a correct answer for my situation. I use Jersey with Jetty and I am not using web.xml. If you try all methods and you don't enable the CORS support, maybe you can try this solution below.
First, define a filter (you can define another one which directly implements Filter class)
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null && request.getMethod().equalsIgnoreCase("OPTIONS");
}
#Override
public void filter(ContainerRequestContext request) throws IOException {
// If it's a preflight request, we abort the request
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
#Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
// if there is no Origin header, we don't do anything.
if (request.getHeaderString("Origin") == null) {
return;
}
// If it is a preflight request, then we add all
// the CORS headers here.
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don't want.
"X-Requested-With,Content-Type,Content-Length,Authorization,"
+ "Accept,Origin,Cache-Control,Accept-Encoding,Access-Control-Request-Headers,"
+ "Access-Control-Request-Method,Referer,x-csrftoken,ClientKey");
}
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
Register this filter to resource config
import java.io.IOException;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
public class AppServer {
public static void main(String[] args) throws Exception {
Server jettyServer = new Server();
// Add port
ServerConnector jettyServerConnector = new ServerConnector(jettyServer);
jettyServerConnector.setPort(Integer.parseInt("9090"));
jettyServer.addConnector(jettyServerConnector);
// Define main servlet context handler
ServletContextHandler jettyServletContextHandler = new ServletContextHandler();
jettyServletContextHandler.setContextPath("/service");
// Define main resource (webapi package) support
ResourceConfig webapiResourceConfig = new ResourceConfig();
webapiResourceConfig.packages("com.example.service");
ServletContainer webapiServletContainer = new ServletContainer(webapiResourceConfig);
ServletHolder webapiServletHolder = new ServletHolder(webapiServletContainer);
jettyServletContextHandler.addServlet(webapiServletHolder, "/webapi/*");
// Add Cors Filter
webapiResourceConfig.register(CorsFilter.class, 1);
try {
jettyServer.start();
jettyServer.dump(System.err);
jettyServer.join();
} catch (Throwable t) {
t.printStackTrace(System.err);
} finally {
jettyServer.destroy();
}
}
}
That's it. This solution solved my problem. Maybe it can be useful for others.

Getting a 403 on root requests when using a ResourceHandler and custom handler in Jetty

In (embedded) Jetty, I'm trying to use a ResourceHandler to serve static files and a custom handler to respond to dynamic requests. Based on this page I have a setup that looks like this:
public static void main(String[] args) throws Exception
{
Server server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(8080);
server.addConnector(connector);
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setDirectoriesListed(false);
resource_handler.setResourceBase(".");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, new MyHandler() });
server.setHandler(handlers);
server.start();
server.join();
}
This works in the sense that it correctly:
Serves up static content from files in my public directory, like /public/style.css
Runs MyHandler on paths that aren't present in the public directory, like /foo/bar
The problem is that I get a 403 in response to the root path (/). MyHandler is capable of responding to those requests, but they get intercepted by the ResourceHandler first. Is there any way to force Jetty to send / requests to MyHandler?
Thanks in advance!
Jetty tries each Handler sequentially until one of the handlers calls setHandled(true) on the request. Not sure why ResourceHandler doesn't do this for "/".
My solution was to reverse the order in which you list the handlers so that yours is called first. Then check for the special case "/" in the URL. If you'd like to pass the request on to the ResourceHandler, simply return without declaring the request as handled.
Declare the order of handlers like this:
Server server = new Server(8080);
CustomHandler default = new CustomHandler();
default.setServer(server);
ResourceHandler files = new ResourceHandler();
files.setServer(server);
files.setResourceBase("./path/to/resources");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] {default, files});
server.setHandler(handlers);
server.start();
server.join();
And define CustomHandler's handle method like this:
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if(!request.getRequestURI().equals("/")){
return;
}
// Do Stuff...
baseRequest.setHandled(true);
return;
}
I agree it would be most elegant to have ResourceHandler simply yield on "/" instead of handling the response with a 403.
My solution:
put MyHandler on a differnt context path than "/" e.g. "/index"
use a rewrite rule to intercept calls to "/" and redirect them to "/index"
The code I use looks like this:
RewriteHandler rewriteHandler = new RewriteHandler();
rewriteHandler.setRewriteRequestURI(true);
rewriteHandler.setRewritePathInfo(false);
rewriteHandler.setOriginalPathAttribute("requestedPath");
RewriteRegexRule rewriteIndex = new RewriteRegexRule();
rewriteIndex.setRegex("^/$");
rewriteIndex.setReplacement("/index.html");
rewriteHandler.addRule(rewriteIndex);
rewriteHandler.setHandler(rootHandlerCollection);
server.setHandler(rewriteHandler);
The regex ensures to only match the exact path, so that "/whatever" is still first handled by the ResourceHandler.