Open mlodato517 opened 1 year ago
Servers like axum and actix-web provide other mechanisms for timeouts when receiving things like request headers
From a tower middleware it's not possible to distinguish between reading the request head timing out or the server handling the request. That requires a specific integration into hyper.
I think if you want a different status you can write your own middleware for it. It's not complicated. If you're using axum it's even easier with axum::middleware::from_fn
.
From a tower middleware it's not possible to distinguish between reading the request head timing out or the server handling the request. That requires a specific integration into hyper.
Totally agreed. Which makes me feel even more like this middleware shouldn't be assuming the error is a client-side error :thinking:
But not all servers are proxies so 504 doesn't seem right to me.
The HyperText Transfer Protocol (HTTP) 504 Gateway Timeout server error response code indicates that the server, while acting as a gateway or proxy, did not get a response in time from the upstream server that it needed in order to complete the request.
But not all servers are proxies so 504 doesn't seem right to me.
Ah totally agreed there - that's why I was hoping to make it configurable. Basically "this middleware is generic and can be used anywhere and so can't pick a status code and be correct all the time". So we can leave the default as 408 so it's not a breaking change but if we give consumers another constructor/method to provide a custom Response
[^1] then it can be used anywhere[^2].
[^1]: Or maybe just a status code for now but a Response
object would be more flexible at the end of the day
[^2]: I'm sure there are places it can't be used but this at least expands where it can be used! :crossed_fingers:
Often I've seen servers return 503 when their own internal timeout triggered (not as a gateway). :)
Sure we can add a method to change the status code.
I don't think we should allow customizing the response body. If people need that I'd recommend writing your own middleware.
Yeah I can see adding the Response
would be annoying because of the generic body. It's a micro-bummer because, in the case of a 503, for instance, the server may want to add a Retry-After header or something but I think a status code is a reasonable compromise for now!
A lot of inspiration for tower came from Finagle, so if there's some prior art for how they handle HTTP timeouts, we could, uh, be inspired again :)
To provide one example, Firefox appears to rely on 408 to mean "client failed to upload the request in time" to retry requests automatically. This makes sense according to the spec as far as I can tell.
This makes returning 408 the entirely wrong thing to do for timeouts caused by server-side processing of the request taking too long: the same slow operation will likely be executed and aborted every time in exactly the same way.
Firefox will retry the request 10 times in my testing when sending 408 so I think we should either change the default or at least make it configurable. I personally would really change the default as that looks like a big footgun.
Feature Request
Motivation
I'd like to be able to return a 504 if the timeout given to the
TimeoutLayer
is exceeded. This is because a 408, which is currently returned, is meant to communicate:I don't know if in general we can tell if the issue is the client sending the request or the server processing the request but in the case of the latter (especially because this service is proxying to another) I'd like to return a 504 Gateway Timeout instead (which is still not a guarantee - the issue could have been the upstream or any post-processing of the upstream's response but that's rare in this case). Servers like
axum
andactix-web
provide other mechanisms for timeouts when receiving things like request headers^1 so maybe something about receiving the request should be handled at the server level or by something likeBodyTimeout
?Proposal
Changing the response status from 408 all the time to 504 all the time would equally be incorrect sometimes and would be a breaking change. Luckily we already have a
new()
constructor forTimeoutLayer
so I imagine that could stay the same and default to 408 and then we could have anew_with_response
constructor that accepts anhttp::Response<B>
or similar that can be returned instead.Alternatives
The most obvious alternative I can see follows from this comment which is to use
tower
instead oftower-http
and explicitly catch the error and perform custom logic on it. This isn't too much code but I wonder if the alternate constructor is valuable because I'm not sure how frequent 408 will be the desired response code (though HTTP semantics are fuzzy enough that maybe it's not worth it!).