Merge pull request #9043 from eclipse/jetty-12.0.x-httpcontentFactoryCleanup

Create StaticHttpContentFactory and other cleanups
This commit is contained in:
Lachlan 2022-12-23 22:54:38 +11:00 committed by GitHub
commit 0e95953be3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 326 additions and 138 deletions

View File

@ -19,6 +19,7 @@ module org.eclipse.jetty.http
exports org.eclipse.jetty.http;
exports org.eclipse.jetty.http.pathmap;
exports org.eclipse.jetty.http.content;
uses org.eclipse.jetty.http.HttpFieldPreEncoder;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.http;
package org.eclipse.jetty.http.content;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -25,6 +25,11 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NoopByteBufferPool;
import org.eclipse.jetty.io.Retainable;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.http;
package org.eclipse.jetty.http.content;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -115,7 +115,8 @@ public class FileMappingHttpContentFactory implements HttpContent.Factory
{
try
{
return BufferUtil.toMappedBuffer(_content.getResource().getPath());
ByteBuffer byteBuffer = BufferUtil.toMappedBuffer(_content.getResource().getPath());
return (byteBuffer == null) ? SENTINEL_BUFFER : byteBuffer;
}
catch (Throwable t)
{

View File

@ -11,13 +11,15 @@
// ========================================================================
//
package org.eclipse.jetty.http;
package org.eclipse.jetty.http.content;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Set;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.MimeTypes.Type;
import org.eclipse.jetty.util.resource.Resource;

View File

@ -11,12 +11,16 @@
// ========================================================================
//
package org.eclipse.jetty.http;
package org.eclipse.jetty.http.content;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Set;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.EtagUtils;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.MimeTypes.Type;
import org.eclipse.jetty.util.resource.Resource;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.http;
package org.eclipse.jetty.http.content;
import java.io.IOException;
import java.util.Arrays;
@ -19,7 +19,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.http.HttpContent.Factory;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.content.HttpContent.Factory;
/**
* This {@link HttpContent.Factory} populates the {@link HttpContent#getPreCompressedContentFormats()} field for any

View File

@ -11,13 +11,19 @@
// ========================================================================
//
package org.eclipse.jetty.http;
package org.eclipse.jetty.http.content;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Set;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.EtagUtils;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.MimeTypes.Type;
import org.eclipse.jetty.util.resource.Resource;

View File

@ -11,12 +11,13 @@
// ========================================================================
//
package org.eclipse.jetty.http;
package org.eclipse.jetty.http.content;
import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.util.Objects;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.util.resource.Resources;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.http;
package org.eclipse.jetty.http.content;
import java.util.Map;
import java.util.Objects;

View File

@ -0,0 +1,77 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.http.content;
import java.io.IOException;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An {@link HttpContent.Factory} implementation which takes a Resource and fakes this resource as
* an entry in every directory. If any request is made for this resources file name, and it is not
* already present in that directory then the resource contained in this factory will be served instead.
*/
public class VirtualHttpContentFactory implements HttpContent.Factory
{
private static final Logger LOG = LoggerFactory.getLogger(VirtualHttpContentFactory.class);
private final HttpContent.Factory _factory;
private final Resource _resource;
private final String _contentType;
private final String _matchSuffix;
public VirtualHttpContentFactory(HttpContent.Factory factory, Resource resource, String contentType)
{
_factory = factory;
_resource = resource;
_matchSuffix = "/" + _resource.getFileName();
_contentType = contentType;
if (LOG.isDebugEnabled())
{
LOG.debug("resource=({}) {}, resource.getFileName()={}", _resource.getClass().getName(), _resource, _resource.getFileName());
}
}
/**
* @return Returns the stylesheet as a Resource.
*/
public Resource getResource()
{
return _resource;
}
@Override
public HttpContent getContent(String path) throws IOException
{
HttpContent content = _factory.getContent(path);
if (content != null)
return content;
if (matchResource(path))
return new ResourceHttpContent(_resource, _contentType);
return null;
}
protected boolean matchResource(String path)
{
return (_resource != null) && (path != null) && path.endsWith(_matchSuffix);
}
@Override
public String toString()
{
return String.format("%s@%x(factory=%s, resource=%s, matchSuffix=%s, contentType=%s)", this.getClass().getName(), this.hashCode(), _factory, _resource, _matchSuffix, _contentType);
}
}

View File

@ -17,7 +17,6 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.InvalidPathException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -30,18 +29,17 @@ import org.eclipse.jetty.http.ByteRange;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.DateParser;
import org.eclipse.jetty.http.EtagUtils;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.MultiPart;
import org.eclipse.jetty.http.MultiPartByteRanges;
import org.eclipse.jetty.http.PreCompressedHttpContent;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.http.QuotedQualityCSV;
import org.eclipse.jetty.http.ResourceHttpContent;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.http.content.PreCompressedHttpContent;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.handler.ContextHandler;
@ -50,7 +48,6 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -78,28 +75,11 @@ public class ResourceService
private boolean _dirAllowed = true;
private boolean _acceptRanges = true;
private HttpField _cacheControl;
private Resource _styleSheet;
public ResourceService()
{
}
/**
* @param stylesheet The location of the stylesheet to be used as a String.
*/
public void setStyleSheet(Resource stylesheet)
{
_styleSheet = stylesheet;
}
/**
* @return Returns the stylesheet as a Resource.
*/
public Resource getStyleSheet()
{
return _styleSheet;
}
public HttpContent getContent(String path, Request request) throws IOException
{
HttpContent content = _contentFactory.getContent(path == null ? "" : path);
@ -134,12 +114,6 @@ public class ResourceService
}
}
}
else
{
// TODO: can this go in a "StaticContentFactory" that goes after ResourceContentFactory?
if ((_styleSheet != null) && (path != null) && path.endsWith("/jetty-dir.css"))
content = new ResourceHttpContent(_styleSheet, "text/css");
}
return content;
}
@ -179,6 +153,12 @@ public class ResourceService
boolean endsWithSlash = pathInContext.endsWith("/");
if (LOG.isDebugEnabled())
{
LOG.debug(".doGet(req={}, resp={}, callback={}, content={}) pathInContext={}, reqRanges={}, endsWithSlash={}",
request, response, callback, content, pathInContext, reqRanges, endsWithSlash);
}
try
{
// Directory?
@ -216,18 +196,13 @@ public class ResourceService
// Send the data
sendData(request, response, callback, content, reqRanges);
}
// Can be thrown from contentFactory.getContent() call when using invalid characters
catch (InvalidPathException e) // TODO: this cannot trigger here, as contentFactory.getContent() isn't called in this try block
catch (Throwable t)
{
if (LOG.isDebugEnabled())
LOG.debug("InvalidPathException for pathInContext: {}", pathInContext, e);
writeHttpError(request, response, callback, HttpStatus.NOT_FOUND_404);
}
catch (IllegalArgumentException e)
{
LOG.warn("Failed to serve resource: {}", pathInContext, e);
LOG.warn("Failed to serve resource: {}", pathInContext, t);
if (!response.isCommitted())
writeHttpError(request, response, callback, e);
writeHttpError(request, response, callback, t);
else
callback.failed(t);
}
}
@ -248,6 +223,12 @@ public class ResourceService
protected void sendRedirect(Request request, Response response, Callback callback, String target)
{
if (LOG.isDebugEnabled())
{
LOG.debug("sendRedirect(req={}, resp={}, callback={}, target={})",
request, response, callback, target);
}
Response.sendRedirect(request, response, callback, target);
}
@ -459,6 +440,12 @@ public class ResourceService
protected void sendWelcome(HttpContent content, String pathInContext, boolean endsWithSlash, Request request, Response response, Callback callback) throws Exception
{
if (LOG.isDebugEnabled())
{
LOG.debug("sendWelcome(content={}, pathInContext={}, endsWithSlash={}, req={}, resp={}, callback={})",
content, pathInContext, endsWithSlash, request, response, callback);
}
// Redirect to directory
if (!endsWithSlash)
{
@ -508,6 +495,11 @@ public class ResourceService
private boolean welcome(Request request, Response response, Callback callback) throws IOException
{
WelcomeAction welcomeAction = processWelcome(request, response);
if (LOG.isDebugEnabled())
{
LOG.debug("welcome(req={}, resp={}, callback={}) welcomeAction={}",
request, response, callback, welcomeAction);
}
if (welcomeAction == null)
return false;
@ -560,6 +552,11 @@ public class ResourceService
private void sendDirectory(Request request, Response response, HttpContent httpContent, Callback callback, String pathInContext)
{
if (LOG.isDebugEnabled())
{
LOG.debug("sendDirectory(req={}, resp={}, content={}, callback={}, pathInContext={})",
request, response, httpContent, callback, pathInContext);
}
if (!_dirAllowed)
{
writeHttpError(request, response, callback, HttpStatus.FORBIDDEN_403);
@ -582,6 +579,12 @@ public class ResourceService
private void sendData(Request request, Response response, Callback callback, HttpContent content, List<String> reqRanges)
{
if (LOG.isDebugEnabled())
{
LOG.debug("sendData(req={}, resp={}, callback={}) content={}, reqRanges={})",
request, response, callback, content, reqRanges);
}
long contentLength = content.getContentLengthValue();
callback = Callback.from(callback, content::release);
@ -640,18 +643,9 @@ public class ResourceService
{
ByteBuffer buffer = content.getByteBuffer();
if (buffer != null)
{
response.write(true, buffer, callback);
}
else
{
// TODO: is it possible to do zero-copy transfer?
// WritableByteChannel c = Response.asWritableByteChannel(target);
// FileChannel fileChannel = (FileChannel) source;
// fileChannel.transferTo(0, contentLength, c);
new ContentWriterIteratingCallback(content, response, callback).iterate();
}
}
catch (Throwable x)
{
@ -833,6 +827,12 @@ public class ResourceService
_redirectWelcome = redirectWelcome;
}
@Override
public String toString()
{
return String.format("%s@%x(contentFactory=%s, dirAllowed=%b, redirectWelcome=%b)", this.getClass().getName(), this.hashCode(), this._contentFactory, this._dirAllowed, this._redirectWelcome);
}
public void setWelcomeFactory(WelcomeFactory welcomeFactory)
{
_welcomeFactory = welcomeFactory;

View File

@ -17,13 +17,14 @@ import java.time.Duration;
import java.util.List;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.http.content.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.content.ResourceHttpContentFactory;
import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.VirtualHttpContentFactory;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NoopByteBufferPool;
import org.eclipse.jetty.server.Context;
@ -59,6 +60,7 @@ public class ResourceHandler extends Handler.Wrapper
private ByteBufferPool _byteBufferPool;
private Resource _resourceBase;
private Resource _styleSheet;
private MimeTypes _mimeTypes;
private List<String> _welcomes = List.of("index.html");
@ -82,8 +84,6 @@ public class ResourceHandler extends Handler.Wrapper
_byteBufferPool = getByteBufferPool(context);
_resourceService.setHttpContentFactory(newHttpContentFactory());
_resourceService.setWelcomeFactory(setupWelcomeFactory());
if (_resourceService.getStyleSheet() == null)
setStyleSheet(getServer().getDefaultStyleSheet());
super.doStart();
}
@ -106,10 +106,11 @@ public class ResourceHandler extends Handler.Wrapper
protected HttpContent.Factory newHttpContentFactory()
{
HttpContent.Factory contentFactory = new ResourceHttpContentFactory(ResourceFactory.of(_resourceBase), _mimeTypes);
contentFactory = new PreCompressedHttpContentFactory(contentFactory, _resourceService.getPrecompressedFormats());
HttpContent.Factory contentFactory = new ResourceHttpContentFactory(ResourceFactory.of(getBaseResource()), getMimeTypes());
contentFactory = new FileMappingHttpContentFactory(contentFactory);
contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, Duration.ofSeconds(1).toMillis(), _byteBufferPool);
contentFactory = new VirtualHttpContentFactory(contentFactory, getStyleSheet(), "text/css");
contentFactory = new PreCompressedHttpContentFactory(contentFactory, getPrecompressedFormats());
contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, Duration.ofSeconds(1).toMillis(), getByteBufferPool());
return contentFactory;
}
@ -160,6 +161,11 @@ public class ResourceHandler extends Handler.Wrapper
return _resourceBase;
}
public ByteBufferPool getByteBufferPool()
{
return _byteBufferPool;
}
/**
* @return the cacheControl header to set on all static content.
*/
@ -186,7 +192,7 @@ public class ResourceHandler extends Handler.Wrapper
*/
public Resource getStyleSheet()
{
return _resourceService.getStyleSheet();
return (_styleSheet == null) ? getServer().getDefaultStyleSheet() : _styleSheet;
}
public List<String> getWelcomeFiles()
@ -339,7 +345,7 @@ public class ResourceHandler extends Handler.Wrapper
*/
public void setStyleSheet(Resource styleSheet)
{
_resourceService.setStyleSheet(styleSheet);
_styleSheet = styleSheet;
}
public void setWelcomeFiles(String... welcomeFiles)

View File

@ -34,20 +34,21 @@ import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.jetty.http.CachingHttpContentFactory;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.EtagUtils;
import org.eclipse.jetty.http.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.http.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.CachingHttpContentFactory;
import org.eclipse.jetty.http.content.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.http.content.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.content.ResourceHttpContentFactory;
import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.VirtualHttpContentFactory;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
@ -665,11 +666,11 @@ public class ResourceHandlerTest
@Override
protected HttpContent.Factory newHttpContentFactory()
{
// For testing the cache should be configured to validate the entry on every request.
HttpContent.Factory contentFactory = new ResourceHttpContentFactory(ResourceFactory.of(getBaseResource()), getMimeTypes());
contentFactory = new PreCompressedHttpContentFactory(contentFactory, getPrecompressedFormats());
contentFactory = new FileMappingHttpContentFactory(contentFactory);
contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, 0, _local.getByteBufferPool());
contentFactory = new VirtualHttpContentFactory(contentFactory, getStyleSheet(), "text/css");
contentFactory = new PreCompressedHttpContentFactory(contentFactory, getPrecompressedFormats());
contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, 0, getByteBufferPool());
return contentFactory;
}
};

View File

@ -22,12 +22,12 @@ import java.nio.file.StandardOpenOption;
import java.util.List;
import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.http.content.ResourceHttpContentFactory;
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Handler;

View File

@ -55,7 +55,21 @@ public class FileID
{
if (uri == null)
return "";
String path = uri.getPath();
if (uri.isOpaque())
{
return getFileName(uri.getSchemeSpecificPart());
}
return getFileName(uri.getPath());
}
/**
* Get the last segment of a String path returning it as the filename
*
* @param path the string path to look for the filename
* @return The last segment of the path
*/
public static String getFileName(String path)
{
if (path == null || "/".equals(path))
return "";
int idx = path.lastIndexOf('/');

View File

@ -106,6 +106,7 @@ public class FileIDTest
Arguments.of(URI.create("file:zed/"), ""),
Arguments.of(URI.create("file:///path/to/test.txt"), "test.txt"),
Arguments.of(URI.create("file:///path/to/dir/"), ""),
Arguments.of(URI.create("jar:file:///home/user/libs/jetty-server-12.jar!/org/eclipse/jetty/server/jetty-dir.css"), "jetty-dir.css"),
Arguments.of(URI.create("http://eclipse.org/jetty/"), ""),
Arguments.of(URI.create("http://eclipse.org/jetty/index.html"), "index.html"),
Arguments.of(URI.create("http://eclipse.org/jetty/docs.html?query=val#anchor"), "docs.html")

View File

@ -23,6 +23,7 @@ import java.util.List;
import java.util.zip.ZipFile;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenPaths;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
@ -73,6 +74,10 @@ public class MountedPathResourceTest
List<String> entries = r.list().stream().map(Resource::getFileName).toList();
assertThat(entries, containsInAnyOrder("alphabet", "numbers", "subsubdir"));
Resource file = r.resolve("subsubdir/numbers");
assertTrue(Resources.isReadableFile(file));
assertThat(file.getFileName(), is("numbers"));
Path extract = workDir.getPathFile("extract");
FS.ensureEmpty(extract);
@ -101,6 +106,36 @@ public class MountedPathResourceTest
}
}
@Test
public void testZipFileName()
{
Path testZip = MavenTestingUtils.getTestResourcePathFile("TestData/test.zip");
String s = "jar:" + testZip.toUri().toASCIIString() + "!/subdir/numbers";
URI uri = URI.create(s);
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
{
Resource r = resourceFactory.newResource(uri);
assertTrue(Resources.isReadableFile(r));
assertThat(r.getFileName(), is("numbers"));
}
}
@Test
public void testJarFileName()
{
Path testZip = MavenPaths.findTestResourceFile("jar-file-resource.jar");
String s = "jar:" + testZip.toUri().toASCIIString() + "!/rez/deep/zzz";
URI uri = URI.create(s);
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
{
Resource r = resourceFactory.newResource(uri);
assertTrue(Resources.isReadableFile(r));
assertThat(r.getFileName(), is("zzz"));
}
}
@Test
public void testJarFileUnMounted() throws Exception
{

View File

@ -46,8 +46,6 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@ -55,9 +53,12 @@ import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.http.content.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.content.ResourceHttpContentFactory;
import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.VirtualHttpContentFactory;
import org.eclipse.jetty.io.ByteBufferInputStream;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NoopByteBufferPool;
@ -131,11 +132,35 @@ public class DefaultServlet extends HttpServlet
{
MimeTypes mimeTypes = servletContextHandler.getMimeTypes();
contentFactory = new ResourceHttpContentFactory(ResourceFactory.of(_baseResource), mimeTypes);
contentFactory = new PreCompressedHttpContentFactory(contentFactory, precompressedFormats);
// Use the servers default stylesheet unless there is one explicitly set by an init param.
Resource styleSheet = servletContextHandler.getServer().getDefaultStyleSheet();
String stylesheetParam = getInitParameter("stylesheet");
if (stylesheetParam != null)
{
try
{
Resource s = _resourceFactory.newResource(stylesheetParam);
if (Resources.isReadableFile(s))
styleSheet = s;
else
LOG.warn("Stylesheet {} does not exist", stylesheetParam);
}
catch (Exception e)
{
if (LOG.isDebugEnabled())
LOG.warn("Unable to use stylesheet: {}", stylesheetParam, e);
else
LOG.warn("Unable to use stylesheet: {} - {}", stylesheetParam, e.toString());
}
}
if (getInitBoolean("useFileMappedBuffer", false))
contentFactory = new FileMappingHttpContentFactory(contentFactory);
contentFactory = new VirtualHttpContentFactory(contentFactory, styleSheet, "text/css");
contentFactory = new PreCompressedHttpContentFactory(contentFactory, precompressedFormats);
int maxCacheSize = getInitInt("maxCacheSize", -2);
int maxCachedFileSize = getInitInt("maxCachedFileSize", -2);
int maxCachedFiles = getInitInt("maxCachedFiles", -2);
@ -175,32 +200,6 @@ public class DefaultServlet extends HttpServlet
else
_welcomeServlets = getInitBoolean("welcomeServlets", _welcomeServlets);
// Use the servers default stylesheet unless there is one explicitly set by an init param.
_resourceService.setStyleSheet(servletContextHandler.getServer().getDefaultStyleSheet());
String stylesheetParam = getInitParameter("stylesheet");
if (stylesheetParam != null)
{
try
{
Resource stylesheet = _resourceFactory.newResource(stylesheetParam);
if (Resources.isReadableFile(stylesheet))
{
_resourceService.setStyleSheet(stylesheet);
}
else
{
LOG.warn("Stylesheet {} does not exist", stylesheetParam);
}
}
catch (Exception e)
{
if (LOG.isDebugEnabled())
LOG.warn("Unable to use stylesheet: {}", stylesheetParam, e);
else
LOG.warn("Unable to use stylesheet: {} - {}", stylesheetParam, e.toString());
}
}
int encodingHeaderCacheSize = getInitInt("encodingHeaderCacheSize", -1);
if (encodingHeaderCacheSize >= 0)
_resourceService.setEncodingCacheSize(encodingHeaderCacheSize);
@ -228,10 +227,15 @@ public class DefaultServlet extends HttpServlet
}
_resourceService.setGzipEquivalentFileExtensions(gzipEquivalentFileExtensions);
// TODO: remove? _servletHandler = _contextHandler.getChildHandlerByClass(ServletHandler.class);
if (LOG.isDebugEnabled())
LOG.debug("base resource = {}", _baseResource);
{
LOG.debug(" .baseResource = {}", _baseResource);
LOG.debug(" .resourceFactory = {}", _resourceFactory);
LOG.debug(" .resourceService = {}", _resourceService);
LOG.debug(" .isPathInfoOnly = {}", _isPathInfoOnly);
LOG.debug(" .welcomeExactServlets = {}", _welcomeExactServlets);
LOG.debug(" .welcomeServlets = {}", _welcomeServlets);
}
}
private static ByteBufferPool getByteBufferPool(ContextHandler contextHandler)
@ -254,7 +258,7 @@ public class DefaultServlet extends HttpServlet
for (String d : deprecated)
{
value = super.getInitParameter(d);
if (value != name)
if (value != null)
{
LOG.warn("Deprecated {} used instead of {}", d, name);
return value;
@ -356,9 +360,16 @@ public class DefaultServlet extends HttpServlet
{
String pathInContext = isPathInfoOnly() ? req.getPathInfo() : URIUtil.addPaths(req.getServletPath(), req.getPathInfo());
boolean included = req.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null;
if (LOG.isDebugEnabled())
LOG.debug("doGet(req={}, resp={}) pathInContext={}, included={}", req, resp, pathInContext, included);
try
{
HttpContent content = _resourceService.getContent(pathInContext, ServletContextRequest.getServletContextRequest(req));
if (LOG.isDebugEnabled())
LOG.debug("content = {}", content);
if (content == null || Resources.missing(content.getResource()))
{
if (included)
@ -427,6 +438,8 @@ public class DefaultServlet extends HttpServlet
@Override
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
if (LOG.isDebugEnabled())
LOG.debug("doHead(req={}, resp={}) (calling doGet())", req, resp);
doGet(req, resp);
}
@ -844,6 +857,8 @@ public class DefaultServlet extends HttpServlet
@Override
public void setStatus(int code)
{
if (LOG.isDebugEnabled())
LOG.debug("{}.setStatus({})", this.getClass().getSimpleName(), code);
_response.setStatus(code);
}
@ -940,6 +955,8 @@ public class DefaultServlet extends HttpServlet
boolean included = request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null;
String welcome = welcomeAction.target();
if (LOG.isDebugEnabled())
LOG.debug("Welcome: {}", welcomeAction);
switch (welcomeAction.type())
{
@ -954,6 +971,8 @@ public class DefaultServlet extends HttpServlet
welcome = URIUtil.addPaths(servletPath, welcome);
response.setContentLength(0);
if (LOG.isDebugEnabled())
LOG.debug("sendRedirect{})", welcome);
response.sendRedirect(welcome); // Call API (might be overridden)
callback.succeeded();
}
@ -989,12 +1008,17 @@ public class DefaultServlet extends HttpServlet
@Override
protected void writeHttpError(Request coreRequest, Response coreResponse, Callback callback, int statusCode)
{
if (LOG.isDebugEnabled())
LOG.debug("writeHttpError(coreRequest={}, coreResponse={}, callback={}, statusCode={})", coreRequest, coreResponse, callback, statusCode);
writeHttpError(coreRequest, coreResponse, callback, statusCode, null, null);
}
@Override
protected void writeHttpError(Request coreRequest, Response coreResponse, Callback callback, Throwable cause)
{
if (LOG.isDebugEnabled())
LOG.debug("writeHttpError(coreRequest={}, coreResponse={}, callback={}, cause={})", coreRequest, coreResponse, callback, cause, cause);
int statusCode = HttpStatus.INTERNAL_SERVER_ERROR_500;
String reason = null;
if (cause instanceof BadMessageException badMessageException)
@ -1008,6 +1032,8 @@ public class DefaultServlet extends HttpServlet
@Override
protected void writeHttpError(Request coreRequest, Response coreResponse, Callback callback, int statusCode, String reason, Throwable cause)
{
if (LOG.isDebugEnabled())
LOG.debug("writeHttpError(coreRequest={}, coreResponse={}, callback={}, statusCode={}, reason={}, cause={})", coreRequest, coreResponse, callback, statusCode, reason, cause, cause);
HttpServletRequest request = getServletRequest(coreRequest);
HttpServletResponse response = getServletResponse(coreResponse);
try

View File

@ -44,7 +44,7 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.content.ResourceHttpContentFactory;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.server.AllowedResourceAliasChecker;
import org.eclipse.jetty.server.HttpConfiguration;
@ -53,6 +53,7 @@ import org.eclipse.jetty.server.ResourceService;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SymlinkAllowedResourceAliasChecker;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenPaths;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
@ -108,8 +109,8 @@ public class DefaultServletTest
connector = new LocalConnector(server);
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
File extraJarResources = MavenTestingUtils.getTestResourceFile(ODD_JAR);
URL[] urls = new URL[]{extraJarResources.toURI().toURL()};
Path extraJarResources = MavenPaths.findTestResourceFile(ODD_JAR);
URL[] urls = new URL[]{extraJarResources.toUri().toURL()};
ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
URLClassLoader extraClassLoader = new URLClassLoader(urls, parentClassLoader);

View File

@ -1,7 +1,9 @@
# Jetty Logging using jetty-slf4j-impl
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.http.LEVEL=DEBUG
#org.eclipse.jetty.server.LEVEL=DEBUG
#org.eclipse.jetty.ee10.servlet.LEVEL=DEBUG
#org.eclipse.jetty.server.ResourceService.LEVEL=DEBUG
#org.eclipse.jetty.io.SocketChannelEndPoint.LEVEL=DEBUG
#org.eclipse.jetty.server.DebugListener.LEVEL=DEBUG
#org.eclipse.jetty.server.internal.HttpChannelState.LEVEL=DEBUG

View File

@ -32,7 +32,7 @@ import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.WriteListener;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.HttpConfiguration;

View File

@ -25,15 +25,16 @@ import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee9.nested.ContextHandler.APIContext;
import org.eclipse.jetty.ee9.nested.ResourceService.WelcomeFactory;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.http.content.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.content.ResourceHttpContentFactory;
import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.VirtualHttpContentFactory;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NoopByteBufferPool;
import org.eclipse.jetty.server.Server;
@ -132,8 +133,9 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
protected HttpContent.Factory newHttpContentFactory()
{
HttpContent.Factory contentFactory = new ResourceHttpContentFactory(this, _mimeTypes);
contentFactory = new PreCompressedHttpContentFactory(contentFactory, _resourceService.getPrecompressedFormats());
contentFactory = new FileMappingHttpContentFactory(contentFactory);
contentFactory = new VirtualHttpContentFactory(contentFactory, getStyleSheet(), "text/css");
contentFactory = new PreCompressedHttpContentFactory(contentFactory, _resourceService.getPrecompressedFormats());
contentFactory = new ValidatingCachingHttpContentFactory(contentFactory, Duration.ofSeconds(1).toMillis(), _byteBufferPool);
return contentFactory;
}

View File

@ -41,14 +41,14 @@ import org.eclipse.jetty.ee9.nested.resource.SeekableByteChannelRangeWriter;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.DateParser;
import org.eclipse.jetty.http.EtagUtils;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.PreCompressedHttpContent;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.http.QuotedQualityCSV;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.http.content.PreCompressedHttpContent;
import org.eclipse.jetty.io.WriterOutputStream;
import org.eclipse.jetty.server.ResourceListing;
import org.eclipse.jetty.util.BufferUtil;

View File

@ -34,7 +34,6 @@ import jakarta.servlet.http.HttpServletResponseWrapper;
import jakarta.servlet.http.HttpSession;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpCookie.SameSite;
import org.eclipse.jetty.http.HttpCookie.SetCookieHttpField;
@ -50,6 +49,7 @@ import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.session.Session;
import org.eclipse.jetty.session.SessionManager;

View File

@ -18,7 +18,7 @@ import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.util.Objects;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.content.HttpContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -30,16 +30,17 @@ import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee9.nested.ContextHandler;
import org.eclipse.jetty.ee9.nested.ResourceService;
import org.eclipse.jetty.ee9.nested.ResourceService.WelcomeFactory;
import org.eclipse.jetty.http.CachingHttpContentFactory;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.CachingHttpContentFactory;
import org.eclipse.jetty.http.content.FileMappingHttpContentFactory;
import org.eclipse.jetty.http.content.HttpContent;
import org.eclipse.jetty.http.content.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.content.ResourceHttpContentFactory;
import org.eclipse.jetty.http.content.ValidatingCachingHttpContentFactory;
import org.eclipse.jetty.http.content.VirtualHttpContentFactory;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NoopByteBufferPool;
import org.eclipse.jetty.server.Server;
@ -250,9 +251,10 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
if (contentFactory == null)
{
contentFactory = new ResourceHttpContentFactory(this, _mimeTypes);
contentFactory = new PreCompressedHttpContentFactory(contentFactory, _resourceService.getPrecompressedFormats());
if (_useFileMappedBuffer)
contentFactory = new FileMappingHttpContentFactory(contentFactory);
contentFactory = new VirtualHttpContentFactory(contentFactory, _styleSheet, "text/css");
contentFactory = new PreCompressedHttpContentFactory(contentFactory, _resourceService.getPrecompressedFormats());
int maxCacheSize = getInitInt("maxCacheSize", -2);
int maxCachedFileSize = getInitInt("maxCachedFileSize", -2);