-
-
Notifications
You must be signed in to change notification settings - Fork 77
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
Non blocking matchers & matching timeout breaking l4 as a listener wrapper #207
Comments
Here is the relevant log:
Notice I increased the timeout to 10 seconds. It won't help as the connect is read in vain. In this case I'm send an http request. |
Feel strange about the prefetch in the loop, if a matcher requires more data, why doesn't it read more in the first place?A deadline will stop it anyway. If a connection requires writing to determine the exact protocol type, it is not suitable to be matched. Except http matcher which can match method, path etc., other matchers can determine based on a few bytes that is available and a conclusion can be made. |
Hey Weidi! Great questions/comments. (I've been recovering from a very tired spell -- I'll try to catch up ASAP.)
Because the client may not send more. What if the matcher requires, say, 10 bytes to match, but the client only sends 3 bytes and then waits for the server to respond?
Hmm this may have been fixed with a recent commit? I don't remember for sure. |
If the connection is written to, the match decision is already made. Matcher should only read from a connection, not write to a it because matcher thinks it's a type of connection. And there is no such thing happening right now in the module, socks5 which this patch is supposed to handle. In this case, if a client sends some data that looks like socks5 proxy, caddy can try to write to it to get more information about the connection, later these data have to be discarded when it is handled by the handler, as he said in that patch:
This is the latest commit. I see he also tries to fix it by using the wrong method in this patch. This time the connection should be handled by a fallback handler, and yet he just limits the number of loops in the vain hope the proxy protocol matcher would match it. The problem is as I said, there is no distinction made between whether the matcher needs more data or just that the fallback handler should be called because non of the matchers matches, non considering that matchers write to connections because matchers presume it's a specific type of protocol (which is not done anyway at this time). |
Yeah I was afraid I would break the listener wrapper. Sorry.
Currently I do not see a way for restoring the old behavior of calling the fallback handler without a match. We need the loop since clients apparently do not always send all data at once (like I mentioned in #72 (comment)).
The point of the changed behavior is to prevent matchers from blocking, so people have to think less about the matcher order. And developers less about how they write matchers. This has nothing to do with matchers writing data.
|
That's a breaking change and unexpected. And as I said, without a fallback in this case causes more unexpected problems. And current codes do nothing to find if the matcher can't match anymore and call the fallback in this case.
That's a positive change. I'll see what can be done with the loop. Right now no fallback and the loop simply doesn't know when to quit as there is no matched routes, and connection is handled by an no op handler is not considered hurts current implementation very much in edge cases. |
This comment almost makes it like ordering is not important and non-blocking is 100% better than the old behavior. Ordering is important, as people expect this behavior for very long time. Less block time is appreciated for drastic different protocols, but handler chains should still be respected. This is also true for caddy http server. |
It's only a problem for the listener wrapper. For which you are very likely the only user since it is neither documented on caddyserver.com nor in the readme of layer4. So one must read the code to even know it exists. I agree breaking changes are bad, but it gets increasingly difficult to keep all config scenarios in mind when developing for layer4. Someday someone should add a kind of integration test suite with some example configs that should work. Additionally, proper version numbers for layer4 would likely also help make changes more visible. For now there is a reason why:
is mentioned in the readme.
Ordering is still somewhat respected, by trying matchers according to order first. Yes I did make a bad decision by jumping back to the 1st matcher initially, but this is fixed by now. Currently I am quite happy how the new behavior works. But I am obviously biased :D |
@ydylla As I mentioned in the other pr, your code doesn't respect if a handler is called instead always tries to rematch. That config has no practical meaning doesn't mean your handling is without problems. Adding an iteration counter doesn't fix the underlying problem. And that's the direct result of not respecting the fallback handler.
It still tries jumping back to 1st matcher if a handler is handled again. And another retry.
But your changes are causing other bugs as well, not just only for the listener wrapper, just as I said, not respecting the fallback handler. There are known caddy community members who use this poorly undocumented feature. Likely only user is such an arrogant presumption. Breaking changes doesn't mean older behaviours shouldn't be restored, which I did in here. In addition to restoring the old fallback behaviour, it addresses the underlying problems that matchers which can't match will be skipped automatically if there is no reason to. I'd like you to take a look at it to say what you think of it. |
@ydylla Can you tell me why we still goto the first handler in this case? |
Not always. Just if the handler is not a terminal one, matching continues (see routes.go#L177). This is for example needed for a route with a tls handler followed by a route with a proxy handler. So layer4 terminates tls and can accept https and http traffic on the same port. [
{
"match": [
{"tls": {"sni": ["localhost"]}}
],
"handle": [
{"handler": "tls"}
]
},
{
"match": [
{"http": [{"host": ["localhost"]}]}
],
"handle": [
{
"handler": "proxy",
"upstreams": [{"dial": ["127.0.0.1:10080"]}]
}
]
}
] On a match the fallback handler is still called. It is passed in routes.go#L166 as
I find it much more intuitve to continue where we left and then start at the beginning again. So its a continuous circle.
Please try the Sadly I still do not see a good way of keeping both behaviors. Its a fundamental problem of who is owning the incoming connection. Using a listener wrapper on top of the caddy http app is also a bit of a hack or at least backwards. It would be much nicer if the caddy http server is a handler of layer4. Like @mholt also mentioned here #70 (comment)
|
I'll see what can be done to make that JSON configuration work. Didn't actually test those. |
I'll provide more concrete configuration examples and corresponding curl commands when I have time and update or create a new issue to show some unexpected breaks (I've some in mind). And discuss them in depth. |
Pull 192 modified routes handling. It works for normal configuration where all possible matchers are predefined.
There is one breaking change in that when compiling a routesList, the next handler is only called when a match is made. As a listener wrapper this means non-matched connections will not be forwarded to the http server. Originally next handler is called if the connection is not matched by any of the matchers in the routes.
Here is an example config that worked in the past, but is now broken (The tls proxy parts work, but local http server doesn't):
@ydylla @mholt I think the original behavior should be restored. If a matcher matches a connection, that connection is handled by the corresponding handler. And if that handler will call the next handler, the handler provider will be called. Otherwise, next is called. Essentially next is a fallback handler.
And, from the messages, it tries to solve when matching, some of the data is not available so a conclusion can be made. But some times a conclusion can be made based on the available data. TLS is one of them. Trying to read a connection again and again in this case makes no sense, wasting server processing powers. There are two solution I think, one a nil match error means more data is needed to read a conclusion, or a special error that means this matcher can never happen. And if all the routes can determine a match can't happen, the next handler will be called.
The text was updated successfully, but these errors were encountered: