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

Fix #188 Clarify the interaction of upgrade with Servlet specification #415

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

markt-asf
Copy link
Contributor

Includes how the HTTP upgrade to WebSocket interacts with:

  • Filters;
  • Listeners; and
  • RequestDispatchers when running on a Servlet container

The test is heavily influenced by how Tomcat currently does things. Happy to adjust where necessary to make it more generally applicable.

@joakime
Copy link
Contributor

joakime commented Jun 29, 2023

Way too specific wording.

WebSocket Upgrade should come after servlet security behaviors (constraints), session behaviors, and be first in the FilterChain (always) IMO.
Too many libraries and things that make horrible assumptions about HTTP requests and never consider the UPGRADE request rules (eg: wrapping).

Controlling where in the FilterChain the upgrade occurs is out of scope for this spec IMO. (containers can support different models, but it shouldn't be codified in this spec)

IMO, In Servlet terms, any use of HttpServletRequestWrapper or HttpServletResponseWrapper should automatically fail an HTTP Upgrade (any kind, websocket or otherwise).

@markt-asf
Copy link
Contributor Author

Way too specific wording.

Do you have suggestions for changes? My aim was to address all the points @gregw raised in the original issue. I was trying to balance being specific enough to address the questions and remove the ambiguity without being more specific than I needed to be to do that.

WebSocket Upgrade should come after servlet security behaviors (constraints), session behaviors, and be first in the FilterChain (always) IMO.

I agree in principle but sometimes some of those behaviours are implemented by Filters so WebSocket upgrade needs to be after them in the FilterChain.

Too many libraries and things that make horrible assumptions about HTTP requests and never consider the UPGRADE request rules (eg: wrapping).

That is an issue for those libraries to address. I don't think we should restrict this spec on the basis that some libraries are poorly designed.

Controlling where in the FilterChain the upgrade occurs is out of scope for this spec IMO. (containers can support different models, but it shouldn't be codified in this spec).

That was my intention with this spec. That the WebSocket upgrade could be anywhere in the FilterChain but this specification doesn't state where. Happy for alternative wording suggestions to make that clearer.

IMO, In Servlet terms, any use of HttpServletRequestWrapper or HttpServletResponseWrapper should automatically fail an HTTP Upgrade (any kind, websocket or otherwise).

I think that is unnecessarily limiting. I'd have no objection to a warning to ensure that Filters wrapping the request/response need to consider the HTTP upgrade to WebSocket case.

@markt-asf
Copy link
Contributor Author

Ping @joakime
This PR has been dormant waiting for feedback for a while.

@joakime
Copy link
Contributor

joakime commented Nov 15, 2023

Way too specific wording.

Do you have suggestions for changes? My aim was to address all the points @gregw raised in the original issue. I was trying to balance being specific enough to address the questions and remove the ambiguity without being more specific than I needed to be to do that.

This PR seems to introduce WebSocket upgrade as a Filter, up until now we didn't dictate how / when an upgrade occurs on a Servlet container in the spec.

WebSocket Upgrade should come after servlet security behaviors (constraints), session behaviors, and be first in the FilterChain (always) IMO.

I agree in principle but sometimes some of those behaviours are implemented by Filters so WebSocket upgrade needs to be after them in the FilterChain.

This PR introduces WebSocket upgrade as a Filter but doesn't define the scope.
It also puts a burden on other Servlet implementations to introduce this functionality (which prior to this PR was not a requirement of the WebSocket spec).

The WebSocket upgrade as Filter use case should either be fully defined by the spec, or not be defined by the spec (like it was before).
The wording in this PR is still too ambiguous.
I'm basing this on the years of answering Jakarta WebSocket questions on stackoverflow.
This WebSocket upgrade as a Filter question has come up repeatedly.

Some general groupings of the questions asked.

  • How to configure order of the WebSocket upgrade filter provided by Server implementation?
  • What kinds of actions before the WebSocket upgrade Filter are acceptable? (wrapping, header modifications, stream wrapping, stream access, stream use, etc)
  • How can a custom WebSocket upgrade filter be written?
    • How can my custom WebSocket upgrade filter be used instead of the Server implementation?
    • How can I disable the automatic WebSocket upgrade in the Server implementation if I want to do it myself?

Too many libraries and things that make horrible assumptions about HTTP requests and never consider the UPGRADE request rules (eg: wrapping).

That is an issue for those libraries to address. I don't think we should restrict this spec on the basis that some libraries are poorly designed.

We should call out these concerns in the spec then.
If the WebSocket upgrade is defined as a Filter, then all Filters before WebSocket as a Filter interacting with the potential WebSocket upgrade must not interfere with the WebSocket upgrade.
This includes not messing with the request headers, or response headers, or wrapping the request input stream/reader, or the response output stream/writer.

Controlling where in the FilterChain the upgrade occurs is out of scope for this spec IMO. (containers can support different models, but it shouldn't be codified in this spec).

That was my intention with this spec. That the WebSocket upgrade could be anywhere in the FilterChain but this specification doesn't state where. Happy for alternative wording suggestions to make that clearer.

Again, I'm seeing a need to define "WebSocket upgrade as a Filter" and what is expected (eg: no messing with websocket request/response headers, no wrapping of streams/readers/writer)

Perhaps even dictating what a websocket server implementation of WebSocket upgrade as a Filter should do if ...

  • it sees a wrapped stream/reader/writer
  • it sees an active stream/reader/writer (meaning those servlet APIs were called)

Should it:

  • Fail the entire request with an Exception?
  • Quietly skip the WebSocket upgrade filter?

We cannot reliably even respond to the upgrade request in this scenarios.

IMO, In Servlet terms, any use of HttpServletRequestWrapper or HttpServletResponseWrapper should automatically fail an HTTP Upgrade (any kind, websocket or otherwise).

I think that is unnecessarily limiting. I'd have no objection to a warning to ensure that Filters wrapping the request/response need to consider the HTTP upgrade to WebSocket case.

Yup, but in a WebSocket upgrade as a Filter section again.

spec/src/main/asciidoc/WebSocket.adoc Outdated Show resolved Hide resolved
…cification

Includes how the HTTP upgrade to WebSocket interacts with:
- Filters;
- Listeners; and
- RequestDispatchers
when running on a Servlet container
@markt-asf
Copy link
Contributor Author

@joakime I've tried to improve the wording based on your feedback. Is it any better? What still needs to be addressed?

@markt-asf
Copy link
Contributor Author

@joakime ping

Copy link
Contributor

@joakime joakime left a comment

Choose a reason for hiding this comment

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

A step in the right direction, still needs improvement.

Comment on lines +1294 to +1297
The WebSocket upgrade *Filter* is positioned in the *FilterChain* after any
**Filter**s defined in the deployment descriptor (*web.xml*). Applications
requiring that the WebSocket upgrade *Filter* is positioned earlier in the
*FilterChain* may define it explicitly in the deployment descriptor.
Copy link
Contributor

Choose a reason for hiding this comment

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

The server defined and implemented Filter should be after Server filters and before deployment descriptor filters, not after.

  1. Server Filters (there are many examples of this: CORS, Forwarding, Security features that don't fit into Servlet Securtity, Auditing, GZIP, etc)
  2. WebSocket Filter
  3. Deployment Descriptor (web.xml and annotations from webapp)

Comment on lines +1306 to +1309
If the response passed to the *doFilter()* method of the WebSocket upgrade
*Filter* has been committed, then the WebSocket upgrade *Filter* will take no
further action and pass the request and response to the next *Filter* in the
*FilterChain*.
Copy link
Contributor

Choose a reason for hiding this comment

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

Good addition. 👍

Comment on lines +1299 to +1304
*Filter* instances earlier in the *FilterChain* may manipulate and/or wrap the
request and/or response and these behaviours may influence the functionality of
the WebSocket upgrade *Filter*. Developers should be aware that some *Filter*
implementations may not have considered HTTP upgrade and configuring such
**Filter**s before the WebSocket upgrade *Filter* in the *FilterChain* may break
the HTTP upgrade process.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the websocket server implementation supplied WebSocket as a Filter unwrap and requests or responses before it upgrades? (I say yes)

Comment on lines +1311 to +1315
If the response passed to the *doFilter()* method of the WebSocket upgrade
*Filter* has not been committed and the WebSocket upgrade *Filter* determines
that the criteria have been met to allow the upgrade to WebSocket to proceed,
then the response will be reset before the WebSocket upgrade *Filter* writes the
appropriate response headers and status code.
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like a duplicate of the paragraph immediately above this.

Comment on lines +1317 to +1322
Applications that require a greater degree of control over the HTTP upgrade
process should use *WsServerContainer.upgradeHttpToWebSocket* method to manually
trigger the HTTP upgrade process to WebSocket. Such applications will probably
wish to avoid the use of WebSocket annotations so that the container does not
automatically deploy WebSocket endpoints as described in
<<application-deployment-on-web-containers>>.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we get a ServletContext attribute that disables/configures specific jakarta.websocket features?

  1. WebSocket Upgrade as a Filter from implementation (options: enable/disable, default is enabled)
  2. WebSocket Upgrade as a Filter location (options: before/after deployment descriptor from webapp, default is before)

I could see some attributes to disable automatic endpoint additions too.
This would be useful for some scenarios.
There's also the scanning of the webapp to support jakarta.websocket.server.ServerApplicationConfig features.

  1. Scan and deploy annotated endpoints.
  2. Scan and report to ServletApplicationConfig entries.

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.

None yet

2 participants