Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jetty-fcgi/fcgi-server/src/main/java/org')
-rw-r--r--jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java55
-rw-r--r--jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java86
-rw-r--r--jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java26
-rw-r--r--jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java92
-rw-r--r--jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java7
5 files changed, 174 insertions, 92 deletions
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java
index 62e3825d7c..58b48b0de5 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java
@@ -18,41 +18,41 @@
package org.eclipse.jetty.fcgi.server;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.fcgi.FCGI;
+import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpInput;
import org.eclipse.jetty.server.HttpTransport;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer>
+public class HttpChannelOverFCGI extends HttpChannel
{
private static final Logger LOG = Log.getLogger(HttpChannelOverFCGI.class);
- private final List<HttpField> fields = new ArrayList<>();
+ private final HttpFields fields = new HttpFields();
private final Dispatcher dispatcher;
private String method;
private String path;
private String query;
private String version;
+ private HostPortHttpField hostPort;
- public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
+ public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport)
{
- super(connector, configuration, endPoint, transport, input);
+ super(connector, configuration, endPoint, transport);
this.dispatcher = new Dispatcher(connector.getExecutor(), this);
}
@@ -67,26 +67,27 @@ public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer>
else if (FCGI.Headers.SERVER_PROTOCOL.equalsIgnoreCase(field.getName()))
version = field.getValue();
else
- fields.add(field);
+ processField(field);
}
- @Override
- public boolean headerComplete()
+ private void processField(HttpField field)
{
- String uri = path;
- if (query != null && query.length() > 0)
- uri += "?" + query;
- startRequest(HttpMethod.fromString(method), method, ByteBuffer.wrap(uri.getBytes(StandardCharsets.UTF_8)),
- HttpVersion.fromString(version));
-
- for (HttpField fcgiField : fields)
+ HttpField httpField = convertHeader(field);
+ if (httpField != null)
{
- HttpField httpField = convertHeader(fcgiField);
- if (httpField != null)
- parsedHeader(httpField);
+ fields.add(httpField);
+ if (HttpHeader.HOST.is(httpField.getName()))
+ hostPort = (HostPortHttpField)httpField;
}
+ }
- return super.headerComplete();
+ public void onRequest()
+ {
+ String uri = path;
+ if (query != null && query.length() > 0)
+ uri += "?" + query;
+ // TODO https?
+ onRequest(new MetaData.Request(method, HttpScheme.HTTP.asString(), hostPort, uri, HttpVersion.fromString(version), fields,Long.MIN_VALUE));
}
private HttpField convertHeader(HttpField field)
@@ -105,7 +106,11 @@ public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer>
httpName.append(Character.toUpperCase(part.charAt(0)));
httpName.append(part.substring(1).toLowerCase(Locale.ENGLISH));
}
- return new HttpField(httpName.toString(), field.getValue());
+ String headerName = httpName.toString();
+ if (HttpHeader.HOST.is(headerName))
+ return new HostPortHttpField(field.getValue());
+ else
+ return new HttpField(httpName.toString(), field.getValue());
}
return null;
}
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java
index 24210d0028..39bc2ead1e 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java
@@ -23,9 +23,9 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.fcgi.generator.Flusher;
import org.eclipse.jetty.fcgi.generator.Generator;
import org.eclipse.jetty.fcgi.generator.ServerGenerator;
-import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.server.HttpTransport;
import org.eclipse.jetty.util.BufferUtil;
@@ -48,65 +48,87 @@ public class HttpTransportOverFCGI implements HttpTransport
}
@Override
- public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
+ public boolean isOptimizedForDirectBuffers()
{
- boolean head = this.head = info.isHead();
- boolean shutdown = this.shutdown = info.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
-
- if (head)
+ return false;
+ }
+
+ @Override
+ public void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback)
+ {
+ if (info!=null)
+ commit(info,head,content,lastContent,callback);
+ else
{
- if (lastContent)
+ if (head)
{
- Generator.Result headersResult = generateResponseHeaders(info, Callback.Adapter.INSTANCE);
- Generator.Result contentResult = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
- flusher.flush(headersResult, contentResult);
+ if (lastContent)
+ {
+ Generator.Result result = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
+ flusher.flush(result);
+ }
+ else
+ {
+ // Skip content generation
+ callback.succeeded();
+ }
}
else
{
- Generator.Result headersResult = generateResponseHeaders(info, callback);
- flusher.flush(headersResult);
+ Generator.Result result = generateResponseContent(content, lastContent, callback);
+ flusher.flush(result);
}
- }
- else
- {
- Generator.Result headersResult = generateResponseHeaders(info, Callback.Adapter.INSTANCE);
- Generator.Result contentResult = generateResponseContent(content, lastContent, callback);
- flusher.flush(headersResult, contentResult);
- }
- if (lastContent && shutdown)
- flusher.shutdown();
+ if (lastContent && shutdown)
+ flusher.shutdown();
+ }
}
@Override
- public void send(ByteBuffer content, boolean lastContent, Callback callback)
+ public boolean isPushSupported()
+ {
+ return false;
+ }
+
+ @Override
+ public void push(org.eclipse.jetty.http.MetaData.Request request)
+ {
+ // LOG.debug("ignore push in {}",this);
+ }
+
+ private void commit(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback)
{
+ this.head = head;
+ boolean shutdown = this.shutdown = info.getFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
+
if (head)
{
if (lastContent)
{
- Generator.Result result = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
- flusher.flush(result);
+ Generator.Result headersResult = generateResponseHeaders(info, Callback.NOOP);
+ Generator.Result contentResult = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback);
+ flusher.flush(headersResult, contentResult);
}
else
{
- // Skip content generation
- callback.succeeded();
+ Generator.Result headersResult = generateResponseHeaders(info, callback);
+ flusher.flush(headersResult);
}
}
else
{
- Generator.Result result = generateResponseContent(content, lastContent, callback);
- flusher.flush(result);
+ Generator.Result headersResult = generateResponseHeaders(info, Callback.NOOP);
+ Generator.Result contentResult = generateResponseContent(content, lastContent, callback);
+ flusher.flush(headersResult, contentResult);
}
if (lastContent && shutdown)
flusher.shutdown();
}
- protected Generator.Result generateResponseHeaders(HttpGenerator.ResponseInfo info, Callback callback)
+ protected Generator.Result generateResponseHeaders(MetaData.Response info, Callback callback)
{
- return generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getHttpFields(), callback);
+ return generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getFields(), callback);
}
protected Generator.Result generateResponseContent(ByteBuffer buffer, boolean lastContent, Callback callback)
@@ -115,13 +137,13 @@ public class HttpTransportOverFCGI implements HttpTransport
}
@Override
- public void abort()
+ public void abort(Throwable failure)
{
aborted = true;
}
@Override
- public void completed()
+ public void onCompleted()
{
}
}
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java
index 9809dd91c8..2b87a91ca9 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java
@@ -29,9 +29,9 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.ByteBufferQueuedHttpInput;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpInput;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -82,11 +82,13 @@ public class ServerFCGIConnection extends AbstractConnection
}
else if (read == 0)
{
+ bufferPool.release(buffer);
fillInterested();
break;
}
else
{
+ bufferPool.release(buffer);
shutdown();
break;
}
@@ -96,11 +98,8 @@ public class ServerFCGIConnection extends AbstractConnection
{
if (LOG.isDebugEnabled())
LOG.debug(x);
- // TODO: fail and close ?
- }
- finally
- {
bufferPool.release(buffer);
+ // TODO: fail and close ?
}
}
@@ -122,8 +121,7 @@ public class ServerFCGIConnection extends AbstractConnection
{
// TODO: handle flags
HttpChannelOverFCGI channel = new HttpChannelOverFCGI(connector, configuration, getEndPoint(),
- new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request, sendStatus200),
- new ByteBufferQueuedHttpInput());
+ new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request, sendStatus200));
HttpChannelOverFCGI existing = channels.putIfAbsent(request, channel);
if (existing != null)
throw new IllegalStateException();
@@ -149,8 +147,8 @@ public class ServerFCGIConnection extends AbstractConnection
LOG.debug("Request {} headers on {}", request, channel);
if (channel != null)
{
- if (channel.headerComplete())
- channel.dispatch();
+ channel.onRequest();
+ channel.dispatch();
}
}
@@ -162,8 +160,9 @@ public class ServerFCGIConnection extends AbstractConnection
LOG.debug("Request {} {} content {} on {}", request, stream, buffer, channel);
if (channel != null)
{
- if (channel.content(buffer))
- channel.dispatch();
+ ByteBuffer copy = ByteBuffer.allocate(buffer.remaining());
+ copy.put(buffer).flip();
+ channel.onContent(new HttpInput.Content(copy));
}
return false;
}
@@ -176,8 +175,7 @@ public class ServerFCGIConnection extends AbstractConnection
LOG.debug("Request {} end on {}", request, channel);
if (channel != null)
{
- if (channel.messageComplete())
- channel.dispatch();
+ channel.onRequestComplete();
}
}
@@ -189,7 +187,7 @@ public class ServerFCGIConnection extends AbstractConnection
LOG.debug("Request {} failure on {}: {}", request, channel, failure);
if (channel != null)
{
- channel.badMessage(400, failure.toString());
+ channel.onBadMessage(400, failure.toString());
}
}
}
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
index b725ccfb9b..8587042304 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
@@ -19,29 +19,35 @@
package org.eclipse.jetty.fcgi.server.proxy;
import java.net.URI;
+import java.util.List;
+import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.proxy.AsyncProxyServlet;
-import org.eclipse.jetty.proxy.ProxyServlet;
/**
- * Specific implementation of {@link ProxyServlet.Transparent} for FastCGI.
- * <p />
+ * Specific implementation of {@link org.eclipse.jetty.proxy.AsyncProxyServlet.Transparent} for FastCGI.
+ * <p>
* This servlet accepts a HTTP request and transforms it into a FastCGI request
* that is sent to the FastCGI server specified in the <code>proxyTo</code>
* init-param.
- * <p />
+ * <p>
* This servlet accepts two additional init-params:
* <ul>
* <li><code>scriptRoot</code>, mandatory, that must be set to the directory where
@@ -64,6 +70,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
{
public static final String SCRIPT_ROOT_INIT_PARAM = "scriptRoot";
public static final String SCRIPT_PATTERN_INIT_PARAM = "scriptPattern";
+ public static final String ORIGINAL_URI_ATTRIBUTE_INIT_PARAM = "originalURIAttribute";
public static final String FASTCGI_HTTPS_INIT_PARAM = "fastCGI.HTTPS";
private static final String REMOTE_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remoteAddr";
@@ -75,6 +82,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
private static final String REQUEST_URI_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestURI";
private Pattern scriptPattern;
+ private String originalURIAttribute;
private boolean fcgiHTTPS;
@Override
@@ -87,6 +95,8 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
value = "(.+?\\.php)";
scriptPattern = Pattern.compile(value);
+ originalURIAttribute = getInitParameter(ORIGINAL_URI_ATTRIBUTE_INIT_PARAM);
+
fcgiHTTPS = Boolean.parseBoolean(getInitParameter(FASTCGI_HTTPS_INIT_PARAM));
}
@@ -101,33 +111,69 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
}
@Override
- protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request)
+ protected void sendProxyRequest(HttpServletRequest request, HttpServletResponse proxyResponse, Request proxyRequest)
{
proxyRequest.attribute(REMOTE_ADDR_ATTRIBUTE, request.getRemoteAddr());
proxyRequest.attribute(REMOTE_PORT_ATTRIBUTE, String.valueOf(request.getRemotePort()));
proxyRequest.attribute(SERVER_NAME_ATTRIBUTE, request.getServerName());
proxyRequest.attribute(SERVER_ADDR_ATTRIBUTE, request.getLocalAddr());
proxyRequest.attribute(SERVER_PORT_ATTRIBUTE, String.valueOf(request.getLocalPort()));
-
proxyRequest.attribute(SCHEME_ATTRIBUTE, request.getScheme());
- // If we are forwarded or included, retain the original request URI.
- String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
- String originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
- if (originalPath == null)
+ // Has the original URI been rewritten ?
+ String originalURI = null;
+ if (originalURIAttribute != null)
+ originalURI = (String)request.getAttribute(originalURIAttribute);
+
+ if (originalURI == null)
{
- originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
- originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
+ // If we are forwarded or included, retain the original request URI.
+ String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
+ String originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
+ if (originalPath == null)
+ {
+ originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
+ originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
+ }
+ if (originalPath != null)
+ {
+ originalURI = originalPath;
+ if (originalQuery != null)
+ originalURI += "?" + originalQuery;
+ }
}
- if (originalPath != null)
- {
- String originalURI = originalPath;
- if (originalQuery != null)
- originalURI += "?" + originalQuery;
+
+ if (originalURI != null)
proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI);
+
+ // If the Host header is missing, add it.
+ if (!proxyRequest.getHeaders().containsKey(HttpHeader.HOST.asString()))
+ {
+ String host = request.getServerName();
+ int port = request.getServerPort();
+ if (!getHttpClient().isDefaultPort(request.getScheme(), port))
+ host += ":" + port;
+ proxyRequest.header(HttpHeader.HOST, host);
+ proxyRequest.header(HttpHeader.X_FORWARDED_HOST, host);
+ }
+
+ // PHP does not like multiple Cookie headers, coalesce into one.
+ List<String> cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE.asString());
+ if (cookies.size() > 1)
+ {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < cookies.size(); ++i)
+ {
+ if (i > 0)
+ builder.append("; ");
+ String cookie = cookies.get(i);
+ builder.append(cookie);
+ }
+ proxyRequest.header(HttpHeader.COOKIE, null);
+ proxyRequest.header(HttpHeader.COOKIE, builder.toString());
}
- super.customizeProxyRequest(proxyRequest, request);
+ super.sendProxyRequest(request, proxyResponse, proxyRequest);
}
protected void customizeFastCGIHeaders(Request proxyRequest, HttpFields fastCGIHeaders)
@@ -183,6 +229,16 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
{
super.customize(request, fastCGIHeaders);
customizeFastCGIHeaders(request, fastCGIHeaders);
+ if (_log.isDebugEnabled())
+ {
+ TreeMap<String, String> fcgi = new TreeMap<>();
+ for (HttpField field : fastCGIHeaders)
+ fcgi.put(field.getName(), field.getValue());
+ String eol = System.lineSeparator();
+ _log.debug("FastCGI variables{}{}", eol, fcgi.entrySet().stream()
+ .map(entry -> String.format("%s: %s", entry.getKey(), entry.getValue()))
+ .collect(Collectors.joining(eol)));
+ }
}
}
}
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java
index 5cdfa1df89..715d65cfc1 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java
@@ -24,6 +24,7 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -36,11 +37,11 @@ import javax.servlet.http.HttpServletResponse;
/**
* Inspired by nginx's try_files functionality.
- * <p />
+ * <p>
* This filter accepts the <code>files</code> init-param as a list of space-separated
* file URIs. The special token <code>$path</code> represents the current request URL's
* path (the portion after the context path).
- * <p />
+ * <p>
* Typical example of how this filter can be configured is the following:
* <pre>
* &lt;filter&gt;
@@ -58,7 +59,7 @@ import javax.servlet.http.HttpServletResponse;
* failing that it will forward the request to <code>index.php?p=/path/to/resource.ext</code>.
* The last file URI specified in the list is therefore the "fallback" to which the request
* is forwarded to in case no previous files can be found.
- * <p />
+ * <p>
* The files are resolved using {@link ServletContext#getResource(String)} to make sure
* that only files visible to the application are served.
*

Back to the top