Skip to content

Commit fc77020

Browse files
committed
Merge remote-tracking branch 'origin/jetty-12.1.x' into fix/jetty-12.1.x/13685-zeroLengthFiles
# Conflicts: # jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/FileMappingHttpContentFactory.java
2 parents b2ebc77 + 8f487bc commit fc77020

File tree

7 files changed

+63
-42
lines changed

7 files changed

+63
-42
lines changed

jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/FileMappingHttpContentFactory.java

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.io.IOException;
1717
import java.nio.ByteBuffer;
1818
import java.nio.file.Path;
19-
import java.time.Instant;
2019
import java.util.Objects;
2120

2221
import org.eclipse.jetty.http.HttpField;
@@ -94,8 +93,6 @@ private static class SingleBufferFileMappedHttpContent extends HttpContent.Wrapp
9493
{
9594
private final ByteBuffer _buffer;
9695
private final HttpField _contentLength;
97-
private final HttpField _lastModified;
98-
private final Instant _lastModifiedInstant;
9996

10097
private SingleBufferFileMappedHttpContent(HttpContent content) throws IOException
10198
{
@@ -107,8 +104,6 @@ private SingleBufferFileMappedHttpContent(HttpContent content) throws IOExceptio
107104
if (_buffer == null)
108105
throw new IOException("Cannot memory map Content (not supported by underlying FileSystem): " + content.getResource());
109106
_contentLength = new HttpField(HttpHeader.CONTENT_LENGTH, Integer.toString(_buffer.remaining()));
110-
_lastModified = content.getLastModified();
111-
_lastModifiedInstant = content.getLastModifiedInstant();
112107
}
113108

114109
@Override
@@ -136,18 +131,6 @@ public long getContentLengthValue()
136131
{
137132
return _buffer.remaining();
138133
}
139-
140-
@Override
141-
public Instant getLastModifiedInstant()
142-
{
143-
return _lastModifiedInstant;
144-
}
145-
146-
@Override
147-
public HttpField getLastModified()
148-
{
149-
return _lastModified;
150-
}
151134
}
152135

153136
private static class MultiBufferFileMappedHttpContent extends HttpContent.Wrapper
@@ -156,8 +139,6 @@ private static class MultiBufferFileMappedHttpContent extends HttpContent.Wrappe
156139
private final int maxBufferSize;
157140
private final HttpField _contentLength;
158141
private final long _contentLengthValue;
159-
private final HttpField _lastModified;
160-
private final Instant _lastModifiedInstant;
161142

162143
private MultiBufferFileMappedHttpContent(HttpContent content, int maxBufferSize) throws IOException
163144
{
@@ -189,8 +170,6 @@ private MultiBufferFileMappedHttpContent(HttpContent content, int maxBufferSize)
189170
}
190171
_contentLengthValue = total;
191172
_contentLength = new HttpField(HttpHeader.CONTENT_LENGTH, Long.toString(total));
192-
_lastModified = content.getLastModified();
193-
_lastModifiedInstant = content.getLastModifiedInstant();
194173
}
195174

196175
@Override
@@ -259,17 +238,5 @@ public long getContentLengthValue()
259238
{
260239
return _contentLengthValue;
261240
}
262-
263-
@Override
264-
public Instant getLastModifiedInstant()
265-
{
266-
return _lastModifiedInstant;
267-
}
268-
269-
@Override
270-
public HttpField getLastModified()
271-
{
272-
return _lastModified;
273-
}
274241
}
275242
}

jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/HttpContent.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public interface HttpContent
9090

9191
/**
9292
* Get the last modified instant of this resource.
93+
* Always return the most up-to-date value.
9394
*
9495
* @return the last modified instant, or null if that instant of this content is not known.
9596
* @see #getLastModified()

jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ValidatingCachingHttpContentFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
* a positive value indicates the number of milliseconds of the minimum time between validation checks.
3939
* </p>
4040
* <p>
41+
* When the wrapped {@link HttpContent}'s {@link HttpContent#getLastModifiedInstant() last modification time} did not change,
42+
* it is considered that the resource did not change too and is therefore not refreshed.
43+
* </p>
44+
* <p>
4145
* This also remember a missed entry for the time set by {@code validationTime}ms. After this has
4246
* elapsed the entry will be invalid and will be evicted from the cache at the next access.
4347
* </p>

jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/content/FileMappingHttpContentFactoryTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.nio.file.Files;
2121
import java.nio.file.Path;
2222
import java.nio.file.StandardOpenOption;
23+
import java.nio.file.attribute.FileTime;
24+
import java.time.Instant;
2325

2426
import org.eclipse.jetty.http.MimeTypes;
2527
import org.eclipse.jetty.io.ByteBufferPool;
@@ -37,13 +39,47 @@
3739

3840
import static org.hamcrest.MatcherAssert.assertThat;
3941
import static org.hamcrest.Matchers.is;
42+
import static org.hamcrest.Matchers.lessThan;
4043
import static org.junit.jupiter.api.Assertions.assertThrows;
4144

4245
@ExtendWith(WorkDirExtension.class)
4346
public class FileMappingHttpContentFactoryTest
4447
{
4548
public WorkDir workDir;
4649

50+
@Test
51+
public void testSingleBufferFileMappedLastModified() throws Exception
52+
{
53+
Path file = Files.writeString(workDir.getEmptyPathDir().resolve("file.txt"), "0123456789abcdefghijABCDEFGHIJ");
54+
FileMappingHttpContentFactory fileMappingHttpContentFactory = new FileMappingHttpContentFactory(
55+
new ResourceHttpContentFactory(ResourceFactory.root().newResource(file.getParent()), MimeTypes.DEFAULTS, ByteBufferPool.SIZED_NON_POOLING));
56+
57+
HttpContent content = fileMappingHttpContentFactory.getContent("file.txt");
58+
Instant originalLastModifiedInstant = content.getLastModifiedInstant();
59+
60+
// Set the file's last modified time to 10s into the future.
61+
Files.setLastModifiedTime(file, FileTime.from(Instant.now().plusSeconds(10)));
62+
63+
assertThat(originalLastModifiedInstant, lessThan(content.getLastModifiedInstant()));
64+
}
65+
66+
@Test
67+
public void testMultiBufferFileMappedLastModified() throws Exception
68+
{
69+
Path file = Files.writeString(workDir.getEmptyPathDir().resolve("file.txt"), "0123456789abcdefghijABCDEFGHIJ");
70+
FileMappingHttpContentFactory fileMappingHttpContentFactory = new FileMappingHttpContentFactory(
71+
new ResourceHttpContentFactory(ResourceFactory.root().newResource(file.getParent()), MimeTypes.DEFAULTS, ByteBufferPool.SIZED_NON_POOLING),
72+
0, 10);
73+
74+
HttpContent content = fileMappingHttpContentFactory.getContent("file.txt");
75+
Instant originalLastModifiedInstant = content.getLastModifiedInstant();
76+
77+
// Set the file's last modified time to 10s into the future.
78+
Files.setLastModifiedTime(file, FileTime.from(Instant.now().plusSeconds(10)));
79+
80+
assertThat(originalLastModifiedInstant, lessThan(content.getLastModifiedInstant()));
81+
}
82+
4783
@Test
4884
public void testMultiBufferFileMappedOffsetAndLength() throws Exception
4985
{

jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/internal/ContentSourceRange.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,15 @@ public Content.Chunk read()
115115
{
116116
// Advance position to the correct offset.
117117
RetainableByteBuffer slice = chunk.slice();
118-
_offsetRemaining -= slice.skip(_offsetRemaining);
119118
chunk.release();
120-
if (_offsetRemaining > 0)
121-
continue;
119+
while (_offsetRemaining > 0)
120+
_offsetRemaining -= slice.skip(_offsetRemaining);
122121
chunk = Content.Chunk.from(slice, chunk.isLast());
123122
}
124123
}
125124

126-
// We can start processing the limited length if we have reached the starting offset.
127-
125+
// We can start processing the limited length if we have reached the starting offset and there
126+
// is length remaining
128127
if (_lengthRemaining == 0)
129128
{
130129
// We have read all we need to

jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Objects;
3030
import java.util.Set;
3131
import java.util.concurrent.CopyOnWriteArrayList;
32+
import java.util.concurrent.atomic.AtomicBoolean;
3233
import java.util.concurrent.atomic.AtomicReference;
3334
import java.util.function.Consumer;
3435
import java.util.function.Predicate;
@@ -136,6 +137,7 @@ public static ContextHandler getContextHandler(Request request)
136137
private final MimeTypes.Mutable _mimeTypes = new MimeTypes.Mutable();
137138
private final List<ContextScopeListener> _contextListeners = new CopyOnWriteArrayList<>();
138139
private final List<VHost> _vhosts = new ArrayList<>();
140+
private final AtomicBoolean _enterScopeSetClassloaderFailed = new AtomicBoolean();
139141

140142
private String _displayName;
141143
private String _contextPath = "/";
@@ -754,8 +756,12 @@ protected ClassLoader enterScope(Request contextRequest)
754756
}
755757
catch (Throwable x)
756758
{
757-
if (LOG.isDebugEnabled())
758-
LOG.debug("error setting a context classloader on thread {}", Thread.currentThread(), x);
759+
// Log as warning the first time it happens.
760+
String msg = "Error setting a context classloader on thread {}";
761+
if (_enterScopeSetClassloaderFailed.compareAndSet(false, true))
762+
LOG.warn(msg, Thread.currentThread(), x);
763+
else if (LOG.isDebugEnabled())
764+
LOG.debug(msg, Thread.currentThread(), x);
759765
}
760766
}
761767
notifyEnterScope(contextRequest);

jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ public boolean handle(Request request, Response response, Callback callback) thr
193193
@Override
194194
public void run()
195195
{
196-
try (Blocker.Callback cb = Blocker.callback())
196+
try (StacklessLogging ignore = new StacklessLogging(ContextHandler.class);
197+
Blocker.Callback cb = Blocker.callback())
197198
{
198199
// When a classloader is configured, Response.write() tries to set it as the context classloader.
199200
response.write(true, ByteBuffer.allocate(32), cb);
@@ -209,6 +210,12 @@ public void run()
209210
}
210211
}
211212

213+
@Override
214+
public ClassLoader getContextClassLoader()
215+
{
216+
return null;
217+
}
218+
212219
@Override
213220
public void setContextClassLoader(ClassLoader cl)
214221
{
@@ -258,7 +265,8 @@ public boolean handle(Request request, Response response, Callback callback) thr
258265
@Override
259266
public void completed(Integer result, Object attachment)
260267
{
261-
try (Blocker.Callback cb = Blocker.callback())
268+
try (StacklessLogging ignore = new StacklessLogging(ContextHandler.class);
269+
Blocker.Callback cb = Blocker.callback())
262270
{
263271
// When a classloader is configured, Response.write() tries to set it as the context classloader.
264272
response.write(true, ByteBuffer.allocate(32), cb);

0 commit comments

Comments
 (0)