-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Modifying of HTTP headers in HttpChannel.Listener#onResponseBegin is no longer possible with Jetty 10 #7818
Comments
FYI, I found a solution using an Interceptor to handle this: final class ModifyHeaderInterceptor implements Interceptor {
private final HttpChannel channel;
private final Interceptor next;
ModifyHeaderInterceptor(HttpChannel channel, Interceptor next) {
this.channel = channel;
this.next = next;
}
private static final Collector<CharSequence,?,String> COMMA_JOINER = Collectors.joining(", ");
private static HttpField mergeHttpFields(String name, List<HttpField> fields) {
return (fields == null) ? null : new HttpField(name, fields.stream()
.map(HttpField::getValues)
.flatMap(Stream::of)
.distinct()
.collect(COMMA_JOINER));
}
@Override
public void write(ByteBuffer content, boolean last, Callback callback) {
if (!channel.isCommitted()) {
final HttpFields.Mutable headers = channel.getResponse().getHttpFields();
headers.computeField("Vary", ModifyHeaderInterceptor::mergeHttpFields);
headers.computeField("Cache-Control", ModifyHeaderInterceptor::mergeHttpFields);
}
next.write(content, last, callback);
}
@Override
public Interceptor getNextInterceptor() {
return next;
}
} I am installing this interceptor inside the This is a workaround but far from elegant. |
Very detailed issue, thank you. Looking at this today. |
…sponse headers Signed-off-by: Joakim Erdfelt <[email protected]>
Signed-off-by: Joakim Erdfelt <[email protected]>
Opened PR #7850 with proposed fix |
I reviewed the PR: looks fine to me. I did no local tests, but the fix was what I expected and the test is basically the code from above. I was just not sure if moving the listener callback may have any other side effects, but after reviwing again the order does not matter (except for this issue). |
Some idea related to this issue:Do you think it might be useful to have a ready-to-use handler/wrapper like this to allow users to "normalize" headers? To me If you think this is a good idea, we could add a ready-to-use implementation in a separate feature request. At least there are multiple ways to do this. |
+ Confirming behavior. Signed-off-by: Joakim Erdfelt <[email protected]>
+ Confirming behavior. Signed-off-by: Joakim Erdfelt <[email protected]>
Signed-off-by: Joakim Erdfelt <[email protected]>
* Issue #7818 - HttpChannel.Listener.onResponseBegin() Test + Confirming behavior. Signed-off-by: Joakim Erdfelt <[email protected]>
Thanks! |
Jetty version(s)
Jetty 10.0.8
Description
I asked this already on the mailing list:
I have a Jetty server that was upgraded from 9.4 to 10.0. There is one requirement to do some "header maintenance": Before the response is sent to client, I want all headers created by several addHeaders in a bunch of servlets, filters and other handlers to be cleaned up. In my case, this is deduplication and merging of "vary" and "cache-control" headers to assist NGINX in front so it only gets one header and not multiples.
In Jetty 9.4, I had the following code, added as bean to the connector:
In 10.0 this no longer compiled, but was easy to fix by using the
HttpFields.Mutable
interface instead of justHttpFields
(Response#getHttpFields()
returns a mutable instance).After trying this out, I noticed that it did not work like it did in 9.4 -- the headers were no longer merged! Debuggig through the code, I figured out that my header merger was actually called before the response was submitted and after it ran, the HttpFields were correctly merged. But on the wire they did not appear, the original headers were sent.
What changed? It looks like it is caused by the change to have mutable vs. immutable
HttpFields
since Jetty 10: Before theonResponseBegin()
handler is called, theHttpChannel
has already made an immutable snapshot of the header fields, so all changes to the mutable ones are lost. I think the problem is inHttpChannel#sendResponse()
: This method callsListener#onResponseBegin()
too late, it should call this before_response.newResponseMetaData()
which makes an immutable snapshot of theHttpFields
: https://github.com/eclipse/jetty.project/blob/a35719367bf67611863f601eb9c150a5881556cc/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java#L1040-L1045I think the problem could be fixed by shuffling the calls a bit. Maybe it would even be better to add another callback in the Listener like:
onBeforeHeadersSend(HttpFields.Mutable fields)
.How to reproduce?
Use the above code in Jetty 9.4: Here the
Vary
andCache-Control
headers are merged usingcomputeField()
, on Jetty 10, the method is called (I added log statements), but due to the call toResponse#newResponseMetaData()
before the callback, the modification to headers never go to the wire (only the snapshotted ones).I think fixing this might be a longer process, but it would be good to have a statement by a developer how to fix this with current Jetty version - or do I need to go back to 9.4 for a while?
The text was updated successfully, but these errors were encountered: