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

Immutable HttpFields and MetaData #4777

Merged
merged 45 commits into from Apr 28, 2020
Merged

Conversation

gregw
Copy link
Contributor

@gregw gregw commented Apr 15, 2020

This is an experimental branch to see the impact of making MetaData immutable. This is currently against 10, but may be for a 10.1 or later version.

Preserve API and usage of HttpFields class while providing a read only interface and immutable implementation.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
Use an ArrayList in HttpFields. While slightly slower than the array, it will mostly be used as a builder pattern for an Immutable

Signed-off-by: Greg Wilkins <gregw@webtide.com>
@gregw
Copy link
Contributor Author

gregw commented Apr 15, 2020

First few commits are about creating an immutable HttpFields. The HttpField class itself was already immutable.

@gregw gregw requested a review from sbordet April 15, 2020 09:27
Fixed exception type.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
asImmutable method

Signed-off-by: Greg Wilkins <gregw@webtide.com>
Made HttpURIU immutable with a builder pattern.
MetaData immutable and working within http module.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
@gregw
Copy link
Contributor Author

gregw commented Apr 16, 2020

@joakime @sbordet @janbartel @lorban @lachlan-roberts I've made HttpFields, HttpURI and MetaData immutable and fixed things up to the http module. It's going to take a while to get everything else fixed, but would be good to get feedback before I commit too much more effort if the tree I'm barking up is not going to work. So if you wanted to review those three classes whilst this is a broken WIP, I'd appreciate any comments etc. (ok not any comments... )

@lorban
Copy link
Contributor

lorban commented Apr 16, 2020

There's a lack of coherency to quickly figure out if a class is immutable or not or can be made immutable. Maybe we should introduce some form of common pattern for classes that can sometimes be mutable or immutable (like the collections' Collection.unmodifiable() for instance) and/or a naming convention to distinguish them?

Fixes from review

Signed-off-by: Greg Wilkins <gregw@webtide.com>
Passing tests upto and including jetty-server

Signed-off-by: Greg Wilkins <gregw@webtide.com>
@gregw
Copy link
Contributor Author

gregw commented Apr 17, 2020

I've pushed changes that makes tests pass up to and including jetty-server. So I think it is worthwhile tacking step back and reviewing the approaches, which as @lorban says, have a "lack of coherency"

Currently I've taken 3 different approaches to immutability:

  1. HttpURI is now immutable itself and has an explicit Builder for mutable operations like:
   HttpURI newUri = new HttpURI.Builder(oldUri).host(serverName).port(serverPort).normalize().build();
  1. HttpFields has taken the opposite approach, keeping that class as a mutable implementing the current API. It has a method asImmutable() that returns a HttpFieldList, which is a new interface that has all the read only methods of HttpFields. This interface is implemented by both HttpFields and it's private HttpFields.Immutable implementation. Because their is no builder pattern, the code to modify is a bit more verbose because there is no chaining:
   HttpFields newFields = new HttpFields(oldFieldList);
   newFields.remove(someHeader);
   newFields.put("Header", "replacement value");
   HttpFieldList newFieldList = newFields.asImmutable();
  1. MetaData, MetaData.Request and MetaData.Response are purely immutable with no builder and only constructors. This is somewhat of a pain in the server as http/1 parser delivers all the components in separate callbacks, so they need to be remembered before making the metadata instance when the headers are complete. This could be a bit more elegant and readable with either a builder pattern or a mutable version of itself.

One immutable variation I have not used is having an API that is read/write, but the immutable version returns a UnsupportedOperationException for write methods. This could perhaps tidy up the HttpFields approach but only if we were able to make HttpFields an interface which would break backwards compatibility.

Also I don't particularly like the Builder pattern being so explicit for HttpURI, so perhaps that could be made a bit cleaning with some utility methods so you could write:

   HttpURI newUri = HttpURI.copy(oldUri).host(serverName).port(serverPort).normalize().toHttpURI();

Perhaps this approach could also be used to add a builder to HttpFields:

   HttpFields.copy(oldFields).remove(someHeader).put("Header","replace value").toHttpFields();

This would make HttpFields immutable, so it would not be possible to have code like:

   HttpFields fields = new HttpFields();
   fields.add("Header", "value");
   fields.add("Host", "localhost");

This would instead need to be written as

   HttpFields fields = HttpFields.empty()
       .add("Header", "value")
       .add("Host", "localhost")
       .toHttpFields();

If we decide to go that way, then I think the same approach would work for MetaData.

I may have a fiddle with this.... but I'd really like a bit more feedback before I commit too much more effort. @sbordet specially for h2 and the client.

@lorban
Copy link
Contributor

lorban commented Apr 17, 2020

For your inspiration, I was thinking about using a pair of marker interfaces that would explicitly state what classes are builders and how to derive a new object from a builder-generated one.

Something like:

public interface Builder<T>
{
  T build();
}

public interface Derivable<B extends Builder<? extends Derivable<?>>>
{
  B derive();
}

which would give us:

public class HttpFieldsBuilder implements Builder<HttpFields>
{
  public HttpFields build()
  {
  ...
  }
}

public class HttpFields implements Derivable<HttpFieldsBuilder>
{
  public HttpFieldsBuilder derive()
  {
  ...
  }
}

public class Sample
{
  public static void main()
  {
    HttpFields original = new HttpFieldsBuilder()
      .add("Header", "value")
      .add("Host", "localhost")
      .build();

    HttpFields copy = original.derive()
      .remove("Header")
      .put("Port", "8080")
      .build();
  }
}

These interfaces would have minimal added value, but would mostly serve as "marker" interfaces, explicitly letting the programmer know that using some class requires following the common builder / derive pattern.

Cleanup of HttpURI.Builder API as suggested in PR.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
Added builder for MetaData.Request

Signed-off-by: Greg Wilkins <gregw@webtide.com>
more api fixes

Signed-off-by: Greg Wilkins <gregw@webtide.com>
WIP making HttpFiels itself immutable.  Currently working up to jetty-servlet.

Need to consider if content-length really is meta data and how much and when can we trust it.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
WIP

Need to consider if content-length really is meta data and how much and when can we trust it. Also need to consider difference between h2 and h1 authority in metadata.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
@gregw
Copy link
Contributor Author

gregw commented Apr 19, 2020

@sbordet can I get a few hours of your time early next week to consider the impact of this on the client and h2.

jetty-client and jetty-servlet passing tests.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
Better align the style of immutability between `HttpFields` and `HttpURI`.
They both now have static build() and from() methods, plus Builder and Immutable implementations.
Potentially `Builder` could be renamed as `Mutable`

Signed-off-by: Greg Wilkins <gregw@webtide.com>
http2-server tests passed

Signed-off-by: Greg Wilkins <gregw@webtide.com>
http2-client tests passed

Signed-off-by: Greg Wilkins <gregw@webtide.com>
cleann build?

Signed-off-by: Greg Wilkins <gregw@webtide.com>
fix

Signed-off-by: Greg Wilkins <gregw@webtide.com>
more test fixes

Signed-off-by: Greg Wilkins <gregw@webtide.com>
…ImmutableMetaData

Signed-off-by: Greg Wilkins <gregw@webtide.com>
review changes

Signed-off-by: Greg Wilkins <gregw@webtide.com>
changes after review:
 + less usage of Mutable
 + more usage of EMPTY
 + restored fragment handling

Signed-off-by: Greg Wilkins <gregw@webtide.com>
changes after review:
 + less usage of Mutable
 + less usage of asImmutable

Signed-off-by: Greg Wilkins <gregw@webtide.com>
changes after review:
 + less usage of Mutable

Signed-off-by: Greg Wilkins <gregw@webtide.com>
changes after review:
 + better handling of URI in ContextHandler

Signed-off-by: Greg Wilkins <gregw@webtide.com>
changes after review:
 + downcast in test to access mutable response headers.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
Copy link
Contributor

@sbordet sbordet left a comment

Choose a reason for hiding this comment

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

Almost there.

changes after review:
 + use put instead of add for one time headers

Signed-off-by: Greg Wilkins <gregw@webtide.com>
@gregw gregw requested a review from sbordet April 24, 2020 16:33
@gregw
Copy link
Contributor Author

gregw commented Apr 27, 2020

@sbordet are we there yet?

Signed-off-by: Greg Wilkins <gregw@webtide.com>
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.

I'm happy with this too.

@joakime
Copy link
Contributor

joakime commented Apr 27, 2020

Last build failed.

@joakime
Copy link
Contributor

joakime commented Apr 27, 2020

Can you merge in the current jetty-10.0.x and let CI build cleanly once?

@gregw gregw merged commit 8c7e34f into jetty-10.0.x Apr 28, 2020
@gregw gregw deleted the jetty-10.0.x-ImmutableMetaData branch April 28, 2020 11:36
@joakime joakime changed the title Jetty 10.0.x immutable meta data Immutable HttpFields and MetaData Dec 5, 2020
rstoyanchev added a commit to spring-projects/spring-framework that referenced this pull request Sep 17, 2021
In Jetty 10, request headers are immutable, see
jetty/jetty.project#4777, but we need to
remove/hide forwarded headers when they have been used.

See gh-27424
lucamilanesio pushed a commit to GerritCodeReview/gerrit that referenced this pull request Jan 15, 2024
Adapt JettyServer to Jetty refactoring
jetty/jetty.project#4777

Bug: Issue 317770594
Release-Notes: Update Jetty to 10.0.19 and servlet-api to 4.0.4
Change-Id: I6faa8b8e13b1525120db79b27ff2a72bf8c5f865
lucamilanesio pushed a commit to GerritCodeReview/gerrit that referenced this pull request Jan 22, 2024
Adapt JettyServer to Jetty refactoring
jetty/jetty.project#4777

Bug: Issue 317770594
Release-Notes: Update Jetty to 10.0.19 and servlet-api to 4.0.4
Change-Id: I6faa8b8e13b1525120db79b27ff2a72bf8c5f865
lucamilanesio pushed a commit to GerritCodeReview/gerrit that referenced this pull request Feb 3, 2024
Adapt JettyServer to Jetty refactoring
jetty/jetty.project#4777

Bug: Issue 317770594
Release-Notes: Update Jetty to 10.0.19 and servlet-api to 4.0.4
Change-Id: I6faa8b8e13b1525120db79b27ff2a72bf8c5f865
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

4 participants