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

#5104 fix protocol version in Via header to work with H2 and other protocols #5107

Conversation

travisspencer
Copy link
Contributor

@travisspencer travisspencer commented Aug 1, 2020

This PR fixes the issue discussed on the user mailing list this week where the Via header was hard coded to use http/1.1.
#5104

@travisspencer
Copy link
Contributor Author

I don't understand why the IP check failed. I've committed before and the only thing I've changed is to add a PGP key. Is that the issue? I didn't see a place to configure that in my Eclipse account. If you note the commit in this PR, the author and sign off are the same as 1d5ceee, for instance. Any clues from those who understand the nuances more than me?

@travisspencer
Copy link
Contributor Author

FYI: I checked and my ECA doesn't expire till 2022.

@sbordet
Copy link
Contributor

sbordet commented Aug 1, 2020

@travisspencer can you please check your ECA is valid for the current ECA version?
E.g. that you filed it for ECA 1.0 but did not update to ECA 2.0?

I searched but could not find an ECA for your email, so we are at a hard stop here.

@WalkerWatch anything we can do to solve this?

@@ -167,7 +167,7 @@ public String getHostHeader()

public String getViaHost()
{
return _viaHost;
return _viaHost == null ? viaHost() : _viaHost;
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't really want to compute viaHost() all the times I call this getter.
I would leave it as it was originally.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we should assign the result to the field to avoid this. As discussed on the mailing list, we're not using this class as a Servlet, so the _viaHost value was null. I wasn't gonna mess with that, but it also came up in the tests where the host was null. I didn't want to expect HTTP/1.1 null, so I did this. I'll fix it to save the results in the field though, so it's only called once.

* the protocol version will be sent. If it is not, the entire protocol (name and version) will be included.
* If the client request includes a Via header, the result will be appended to that to form a chain.
*
* @since 9.4.32
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove @since, we don't track these things.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thought not. Removed.

viaHeaderValue += protocolName + " " + getViaHost();

proxyRequest.header(HttpHeader.VIA, viaHeaderValue);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This method must call the old deprecated one.

Solution1:

  • get Via header value
  • call deprecated addViaHeader() method (now empty)
  • get again Via header value; if it's changed, do nothing, else your new logic.

Solution2:

  • call deprecated addViaHeader() method (now empty)
  • your new logic

Opinions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why? This is called from addProxyHeaders. For 1.1 clients, it's the same header value with one small change -- the protocol name for 1.1 is now uppercase. That's another bug though. The protocol is case sensitive and the registered name in IANA is exactly HTTP not http. This fixes clients for 2.0 and other protocols. I think this is good to go. If you still disagree, can you elaborate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I see what you mean. They overrode addViaHeader and it's not called anymore by addProxyHeaders.

Hum...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated. How's that?


};
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Inline these 2 methods, they are only used once.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doing so obscures the point of the test, and I'd really rather not. Though currently only used once, they make the test more readable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nope. The test is less readable and using mocks obscures the test. Remove them.

@travisspencer
Copy link
Contributor Author

@travisspencer can you please check your ECA is valid for the current ECA version?
E.g. that you filed it for ECA 1.0 but did not update to ECA 2.0?

I've signed Version 3.0.0 October 22, 2018. When I search, I can't find newer. I wonder if it's something else.

I searched but could not find an ECA for your email, so we are at a hard stop here.

My Eclipse username is tspencer5c1 if that helps find things.

@travisspencer travisspencer force-pushed the fix/jetty-9.4.x/5104-incorrect-protocol-version-in-Via-header branch 4 times, most recently from b6ec5ff to 93c777e Compare August 1, 2020 19:49
@travisspencer
Copy link
Contributor Author

On the ECA issues you can see from my Eclipse account that my email in the author field of the commit matches (what I can see at least) in my profile:

Screenshot 2020-08-01 at 9 55 23 PM

@joakime
Copy link
Contributor

joakime commented Aug 1, 2020

On the ECA issues you can see from my Eclipse account that my email in the author field of the commit matches (what I can see at least) in my profile:

What is the correct username for your eclipse account.

As the one you mentioned earlier results in a 404 - https://accounts.eclipse.org/users/tspencer5c1/eca

Here's mine, for example - https://accounts.eclipse.org/users/jerdfelt/eca

@travisspencer
Copy link
Contributor Author

That URL to my account opens fine, @joakime, after I login, and yours is a 403 (for me). Here it is:

Screenshot 2020-08-02 at 10 00 02 AM

My email in my Eclipse account (see previous comment with screenshot) matches the one in the commit. It's a little hard to see in GitHub, but it's this:

commit 93c777eb8c8fd6e85c4e659a8a6c6b5725b2de51 (HEAD -> fix/jetty-9.4.x/5104-incorrect-protocol-version-in-Via-header, origin/fix/jetty-9.4.x/5104-incorrect-protocol-version-in-Via-header)
gpg: Signature made Sat Aug  1 21:49:29 2020 CEST
gpg:                using RSA key 52B8B12E4022D5748175BB29E4EB8B447442E467
gpg: Good signature from "Travis Spencer <travis@curity.io>" [ultimate]
gpg:                 aka "Travis Spencer <travis.spencer@curity.io>" [ultimate]
gpg:                 aka "[jpeg image of size 10978]" [ultimate]
Author: Travis Spencer <travis@curity.io>
Date:   Sat Aug 1 11:25:48 2020 +0200

    Issue #5104 - Fix protocol version in Via header to work with H2 and other protocols

    Bug: https://github.com/eclipse/jetty.project/issues/5104
    Signed-off-by: Travis Spencer <travis@curity.io>

This also matches what's in my GitHub profile:

Screenshot 2020-08-02 at 10 07 56 AM

I have passed these checks in multiple, previous PRs, like Pull Request #4129. I am starting to think that this doesn't have anything to do with my account but the ECA check. Could that be the case?

@joakime
Copy link
Contributor

joakime commented Aug 2, 2020

This is super strange.

Leave this PR open!

I'm going to file an issue with the Eclipse side about the ECA validation on this PR.

See https://bugs.eclipse.org/bugs/show_bug.cgi?id=565761

oldAddViaHeaderCalled = false;
addViaHeader(proxyRequest);

if (!oldAddViaHeaderCalled)
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 like this test, pl;us I don't think it works if super is called. I think you need to use reflection in init() to see if the method has been over ridden. Then I think you should still do your checking for non http/1 proxy Request and only call the old method IF over ridden AND HTTP/1, otherwise always do it the new way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It works in this test at least:

@Test
public void testInheritance() throws Exception
{
    ProxyServlet derivedProxyServlet = new ProxyServlet()
    {
        @Override
        protected void addViaHeader(Request proxyRequest)
        {
            System.err.println("addViaHeader called: " + proxyRequest);
            super.addViaHeader(proxyRequest);
        }
    };

    startServer(new HttpServlet()
    {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
        {
            PrintWriter writer = resp.getWriter();
            writer.write(req.getHeader("Via"));
            writer.flush();
        }
    });
    String viaHost = "my-good-via-host.example.org";
    startProxy(derivedProxyServlet, Collections.singletonMap("viaHost", viaHost));
    startClient();

    HttpRequest proxyRequest = mockProxyRequest();
    derivedProxyServlet.addViaHeader(proxyRequest);

    ContentResponse response = client.GET("http://localhost:" + serverConnector.getLocalPort());

    assertThat("Response expected to contain content of Via Header from the request",
               response.getContentAsString(),
               Matchers.equalTo("1.1 " + viaHost));
}

This assumes we want to fix the other issues I mentioned that http as the protocol name was wrong and that we should not include protocol name nor the / when HTTP is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added this test an another that does not call super. Can you recheck, @gregw ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah it is the other way around. If somebody implements addViaHeader(Request proxyRequest) that doesn't call super, then you assume they have taken care of it. OK.... I guess that works, but it is rather ugly.... hmm but so is reflection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess that works, but it is rather ugly

It is certainly ugly. I think it wouldn't stick out as much if I would have done a check for the present of the Via header at this point, but that seemed to have a running time of O(n). That's why I added this flag. It's a micro-optimization, but I'll take it.

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 this can be fixed in general.
A subclass calling super and expecting the header to be there will break (e.g. NPE).
Broken for broken, I'd say to implement the method empty, don't bother with the boolean flag and add the new logic.

… and other protocols

Bug: jetty#5104
Signed-off-by: Travis Spencer <travis@curity.io>
@travisspencer travisspencer force-pushed the fix/jetty-9.4.x/5104-incorrect-protocol-version-in-Via-header branch from 93c777e to f071325 Compare August 3, 2020 06:15
@joakime
Copy link
Contributor

joakime commented Aug 4, 2020

Still waiting on Eclipse to address the ECA validation concerns here.

@travisspencer
Copy link
Contributor Author

While we wait on the ECA issues, @sbordet , is there anything else you think should be done to the code?

@gregw
Copy link
Contributor

gregw commented Aug 11, 2020

@sbordet can you re-review.... I know ECA still has problems, but I can see enough evidence here that it has been signed, so I think we could merge OK

@joakime
Copy link
Contributor

joakime commented Aug 11, 2020

I know ECA still has problems, but I can see enough evidence here that it has been signed, so I think we could merge OK

Go ahead and comment on the open issue about this ECA validation wonkiness - https://bugs.eclipse.org/bugs/show_bug.cgi?id=565761

@@ -549,6 +612,79 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
Matchers.equalTo("localhost:" + serverConnector.getLocalPort()));
}

@ParameterizedTest
@MethodSource("subclassesWithProtocols")
public void testInheritance(ProxyServlet derivedProxyServlet, String protocol) throws Exception
Copy link
Contributor

Choose a reason for hiding this comment

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

Please no.
It's impossible to read this test and understand what's doing without jumping in 3 other different places in the code.
Don't use mock anywhere, not needed and too brittle.
Don't use @ParameterizedTest, not needed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alt-Space in Intellij and I can see the implementation of all 3 of those other methods without leaving the test. Having code in various places isn't a problem IHMO, especially when that code does only 1 thing. I want tests that are easy to read and understand the intent. Cruft obscures that and makes the test harder to understand. This is your guys party, and I will change this if you insist.

If this test isn't parameterized, then compatibility with all existing subclasses isn't tested. In which case, the test can be deleted.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh the irony of somebody using some intellij key sequence in discussion with @sbordet, who is normally the one to say that ctrl-rightalt-elbow-headbutt will solve your problems! @travisspencer you just made my day :)

But seriously, I too am not a fan of mock in test harness and in this case I find the method names confusing as it looks like they will return some third party mock library generated test classes. But they don't, they just return simple instantiations of the real classes. So in this case I too think it would be simpler to inline them and avoid the confusion of a "mock" name in a method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll shift the code around as @sbordet suggested. My consolation is that I made your day, @gregw .

}
});
String viaHost = "my-good-via-host.example.org";
startProxy(derivedProxyServlet, Collections.singletonMap("viaHost", viaHost));
Copy link
Contributor

Choose a reason for hiding this comment

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

Just inline the ProxyServlet subclass here.

startClient();

HttpRequest proxyRequest = mockProxyRequest();
derivedProxyServlet.addViaHeader(proxyRequest);
Copy link
Contributor

Choose a reason for hiding this comment

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

These 2 lines are useless?

assertThat("Response expected to contain a Via header with the right protocol version and host",
proxyRequest.getHeaders().getField("Via").getValue(),
Matchers.anyOf(Matchers.equalTo(expectedVia), Matchers.equalTo(expectedViaWithLocalhost)));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Please no.
This is not testing ProxyServlet, it is just testing that this test calls addViaHeader(), which well... it does.

If by mistake the real call to addViaHeader() is removed from ProxyServlet (e.g. a bad merge or some mistake), this test will pass, but ProxyServlet won't work.

Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it testing that addViaHeader actually does add the Via header correctly for all the protocols?
I think this test to check the method in isolation is fine. OK it is also tested by the previous test that checks the whole servlet, but testing the method directly is OK as well... so long as it doesn't need a third party mock library to do so.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya, that's what it's doing @gregw . I agree that it's helpful in that sense.

oldAddViaHeaderCalled = false;
addViaHeader(proxyRequest);

if (!oldAddViaHeaderCalled)
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 this can be fixed in general.
A subclass calling super and expecting the header to be there will break (e.g. NPE).
Broken for broken, I'd say to implement the method empty, don't bother with the boolean flag and add the new logic.


};
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Nope. The test is less readable and using mocks obscures the test. Remove them.

@sbordet
Copy link
Contributor

sbordet commented Aug 11, 2020

@travisspencer let's do this:

  • in AbstractProxyServlet.addProxyHeaders(), just before calling addViaHeader(proxyRequest), set the clientRequest as an attribute in proxyRequest; use key AbstractProxyServlet.CLIENT_REQUEST_ATTRIBUTE since it's already there.
  • in addViaHeader(proxyRequest), remove the attribute so now you have the clientRequest, and then call your new method with 2 parameters.

This will solve the override issue. We don't need anymore tests for the override behavior, etc. so remove those too.

Implement the new logic in your new method (well, you've done it already) and let's verify it works with an integration test (not a unit test).
For HTTP/2 there is a test class in http2-client called ProxyTest - you should be able to add a test in there for the Via header functionality.

Maybe close this PR and open a new one, in the hope that it will get your ECA correctly. Thanks!

@joakime
Copy link
Contributor

joakime commented Aug 11, 2020

Maybe close this PR and open a new one, in the hope that it will get your ECA correctly. Thanks!

The ECA issue will still exist, as even the manual verification steps against his userid and email address still fail.

@sbordet
Copy link
Contributor

sbordet commented Aug 12, 2020

@travisspencer I apologize for being too harsh in my review of your PR - bad word choice on my part.

Have you seen my proposal in the comment above?
I think it'll solve most of the issues and be much cleaner.

In addition to the suggestion above, I think we can always add clientRequest as an attribute, not only when there is an Expect: 100-continue, which means removing what subclasses do now, and add clientRequest as an attribute in AbstractProxyServlet.

You keen on doing that, perhaps in a different PR?

@travisspencer
Copy link
Contributor Author

I apologize for being too harsh in my review of your PR

No apology needed, @sbordet . I wasn't bothered by anything you said or the way you said it.

Have you seen my proposal in the comment above?

Yes, but I'm preoccupied with other tasks today, so I haven't had time to internalize it enough to reply yet.

Please feel free to jump in here and make a PR on top of mine if you have more bandwidth then me. My ETA is probably end of week.

@sbordet
Copy link
Contributor

sbordet commented Aug 12, 2020

Please feel free to jump in here and make a PR on top of mine

Will do today.

@joakime
Copy link
Contributor

joakime commented Aug 12, 2020

@travisspencer can you validate your email address entries as outlined in comment 4 in the ECA issue -> https://bugs.eclipse.org/bugs/show_bug.cgi?id=565761#c4

@sbordet
Copy link
Contributor

sbordet commented Aug 12, 2020

Closed in favor of #5144.

@sbordet sbordet closed this Aug 12, 2020
@travisspencer
Copy link
Contributor Author

Fixed the ECA issues @joakime -- see comment here: #5144 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

AbstractProxyServlet include incorrect protocol version in Via header when accessed over H2
4 participants