Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #12695 - don't return ByteBuffer from .toMappedBuffer if FS reports as UnsupportedOperationException. #12696

Merged
merged 7 commits into from
Jan 15, 2025

Conversation

joakime
Copy link
Contributor

@joakime joakime commented Jan 9, 2025

To fix issue with UnsupportedOperationException, just have toMappedBuffer return null (that is an existing contract) if that exception is triggered.

@joakime joakime added the Bug For general bugs on Jetty side label Jan 9, 2025
@joakime joakime requested a review from lorban January 9, 2025 21:00
@joakime joakime self-assigned this Jan 9, 2025
@joakime
Copy link
Contributor Author

joakime commented Jan 9, 2025

This is an incomplete fix, as the usages of toMappedBuffer in org.eclipse.jetty.http.content.FileMappingHttpContentFactory incorrectly assume that they will always get a mapped ByteBuffer.

+ Fallback to normal filesystem read if memory mapping is not supported.
@@ -262,17 +262,25 @@ public static Response parseResponse(Input input) throws IOException
return parseResponse(input, false);
}

public static Response parseResponse(Input input, Response response) throws IOException
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While restoring FileBufferedResponseHandlerTest (from earlier versions of Jetty) this method in HttpTester was also restored.

@joakime joakime marked this pull request as ready for review January 13, 2025 12:44
+ Add new .setUseFileMapping(boolean) flag
  (defaults to true)
+ Add test case variations for non-mapping
+ Fix position calculation
@joakime joakime requested a review from lorban January 13, 2025 20:44
Copy link
Contributor

@lorban lorban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this whole implementation could be replaced with a simple Content.copy()?

Writing a Content.Sink that does _interceptor.getNextInterceptor().write(buffer, last, callback) looks trivial; so if a memory-mapping Content.Source could be created, with a fallback to Content.Source.from(Path) if mmap is disabled or unavailable could be written then the whole Content logic could be re-used.

+ Remove useFileMapping config
+ Remove custom IteratingCallback
+ Use Content.Source.from(Path)
+ New InterceptorSink
@joakime joakime requested a review from lorban January 14, 2025 16:01
Copy link
Contributor

@lorban lorban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave it up to you to decide to keep the new ByteBufferPool.Sized.as() helper or not, as well as inlining the InterceptorSink or not.

LGTM either way.

* @param size The specified size in bytes of the buffer, or -1 for a default
* @return the pool as a sized pool
*/
public static ByteBufferPool.Sized as(ByteBufferPool pool, boolean direct, int size)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I like this new helper as it takes parameters that could be ignored without any hint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

private final long fileLength = _filePath.toFile().length();
private long _pos = 0;
private boolean _last = false;
ByteBufferPool.Sized sizedPool = ByteBufferPool.Sized.as(getServer().getByteBufferPool(), true, getBufferSize());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd rather see a new instance of ByteBufferPool.Sized being created.

_pos += len;
return Action.SCHEDULED;
}
private static class InterceptorSink implements Content.Sink
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could inline this class as a simple lambda:

Content.Sink sink = (last, byteBuffer, callback) -> _interceptor.write(last, byteBuffer, callback);

private long _pos = 0;
private boolean _last = false;
ByteBufferPool.Sized sizedPool = ByteBufferPool.Sized.as(getServer().getByteBufferPool(), true, getBufferSize());
Content.Source source = Content.Source.from(sizedPool, _filePath);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not mmap files naymore, but honestly I think it is for the best.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supporting mmap from Content.Source.from(path) is still possible, but in a future PR.

@joakime joakime requested a review from lorban January 14, 2025 18:30
@joakime joakime merged commit e89d1e6 into jetty-12.1.x Jan 15, 2025
10 checks passed
@joakime joakime deleted the fix/12.1.x/unsupported-tomappedbuffer branch January 15, 2025 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug For general bugs on Jetty side
Projects
Status: ✅ Done
2 participants