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

Experiment/12/improve default servlet #10222

Merged
merged 14 commits into from
Aug 17, 2023

Conversation

gregw
Copy link
Contributor

@gregw gregw commented Aug 4, 2023

  • don't wrap the httpServletRequest unless necessary due to wrapping
  • don't wrap the httpServletResponse unless necessary due to wrapping
  • send content asynchronously if large and unfiltered
  • Remove unused boolean return from ServletChannel.handle
  • added TODOs where range request handling could calculate content length

 + don't wrap the httpServletRequest unless necessary due to wrapping
 + don't wrap the httpServletResponse unless necessary due to wrapping
 + send content asynchronously if large and unfiltered
 + Remove unused boolean return from ServletChannel.handle
 + added TODOs where range request handling could calculate content length
 + Call multipartlength, even though it is always -1
@gregw gregw requested review from sbordet and lorban August 4, 2023 05:48
@gregw
Copy link
Contributor Author

gregw commented Aug 4, 2023

@lorban @sbordet @lachlan-roberts this PR improves the efficiency of the DefaultServlet.

However, it has affected some range tests that were expecting content-length to be set, but that was only because the combined parts are less that an output buffer in size. It would be better if the mutlti-part was able to calculate the length. Perhaps it needs to pre-generate the part headers? But OK for now without the content-length

Cleanup downcasting
Cleanup downcasting
Protect close after lasts
Prefer the wrapped servlet request/response
Do not use Wrappers for the wrapped servlet request/response
 + Use static for bytes written
@gregw
Copy link
Contributor Author

gregw commented Aug 14, 2023

@lorban welcome back. Can you review this as a priority. I think it is a good cleanup of the default servlet and removes the intrinsic blocking that itches at my sole.

@gregw
Copy link
Contributor Author

gregw commented Aug 14, 2023

@lachlan-roberts @sbordet would be good to get a review on this as well.

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.

One thing that's making me scratch my head:

Now that ServletCoreRequest|Response have been extracted as top-level classes next to ServletApiRequest|Response I wonder if their names and proximity won't add confusion in the future?

@gregw
Copy link
Contributor Author

gregw commented Aug 14, 2023

One thing that's making me scratch my head:

Now that ServletCoreRequest|Response have been extracted as top-level classes next to ServletApiRequest|Response I wonder if their names and proximity won't add confusion in the future?

Any suggestions? ServletRequestRequest? Would adding the word Wrapper help?

@lorban
Copy link
Contributor

lorban commented Aug 14, 2023

Since they are implementations, how about very long names?
ServletCoreRequest - > ServletDelegatingCoreRequest (IS-A core Request; delegates to servlet api)
ServletApiRequest -> CoreDelegatingServletRequest (IS-A ServletRequest; delegates to core api)

@gregw
Copy link
Contributor Author

gregw commented Aug 14, 2023

@lorban

Since they are implementations, how about very long names?
ServletCoreRequest - > ServletDelegatingCoreRequest (IS-A core Request; delegates to servlet api)
ServletApiRequest -> CoreDelegatingServletRequest (IS-A ServletRequest; delegates to core api)

I don't think "Delegating" adds very much....

We could do CoreRequestServletRequest and ServletRequestCoreRequest... or CoreServletRequest and ServletCoreRequest. but I also kind of like ServletApiRequest as it says what it does and we use the "Api" name elsewhere to indicate when we are wrapping for API reasons.

@gregw gregw requested a review from lorban August 14, 2023 12:50
updates from review
lorban
lorban previously approved these changes Aug 15, 2023
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 cannot come up with better *Request class names, so the current ones probably are reasonable.

# Conflicts:
#	jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/AsyncContextState.java
#	jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannel.java
@gregw
Copy link
Contributor Author

gregw commented Aug 15, 2023

CI failure is a flaky test... giving it one more spin....

@gregw
Copy link
Contributor Author

gregw commented Aug 16, 2023

@lorban can you re review after I updated from merge conflict
@sbordet @lachlan-roberts 2nd and 3rd opinions welcome

default ->
{
if (BufferUtil.isEmpty(content))
callback.succeeded();
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the case where this happens?
If last is true do we not need to do anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@lachlan-roberts This can happen is something does a flush when last has already been sent. This can be needed when checking there is no data buffered in a writer.

{
if (sendErrorOrAbort("Insufficient content written"))
long written = getBytesWritten();
Copy link
Contributor

Choose a reason for hiding this comment

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

This getBytesWritten() implementation is going directly to the ChannelResponse, which will be the compressed content length if Gzip is used.

Is it possible that the ServletContextResponse detects the content is incomplete because it was expecting the uncompressed content length from getBytesWritten()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@lachlan-roberts The code is comparing the actual bytes written to the actual content-length header (if any). Either they both represent compressed content or they both don't.

@@ -2183,7 +2183,7 @@ public static Stream<Arguments> rangeScenarios()
String body = response.getContent();

assertThat(response, containsHeaderValue("Content-Type", "multipart/byteranges"));
assertThat(response, containsHeaderValue("Content-Length", "" + body.length()));
// TODO #10307 assertThat(response, containsHeaderValue("Content-Length", String.valueOf(body.length())));
Copy link
Contributor

Choose a reason for hiding this comment

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

Why were these tests passing before but not now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@lachlan-roberts Because the content was written via the servlet HttpOutput which aggregated them and they were small enough examples to fit into a single buffer. Thus the content length was known. Now it is written without the copy, but that means the length is not known when the response is committed.

See issue.

@gregw gregw merged commit 5aea1e4 into jetty-12.0.x Aug 17, 2023
2 checks passed
@gregw gregw deleted the experiment/12/improve-default-servlet branch August 17, 2023 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants