-
Notifications
You must be signed in to change notification settings - Fork 596
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
Add automatic redirection handling #195
Comments
I've just stumbled across the need for this and will have to of course hand craft it. I just thought that you'd like to know of at least one person who needs this functionality. |
Another case to handle would be redirect loops |
Also +1 for this feature. Did tracking of this issue get lost? Was 1.0-RC4, now I see it has no milestone. Yet I don't see that anyone has modified the milestone since sirthias on Jun 24. |
Also +1 from me. |
Logging a +1 ☝️ February 24, 2016 3:11 PM We'd welcome contributions of this feature, anyone interested in helping out? :-) |
I'd like to try to help with this, since I need it in my project. However, I'm totally new to akka source code, so I'd definitely need some initial guidance. What would be the best channel to ask questions related to this? |
Thank a lot for offering help on that :-) |
Thanks, I'd be happy to help. Just not sure if I'm up to this task, but one never knows unless one tries. :) Ok, here are the initial questions.
This is just to get my thoughts straight before I can begin even thinking of possible solutions. |
@RomanIakovlev You probably also need to consider adding an option to decide which headers are re-submitted when the re-direct is followed. Spray used to drop the headers, which is apparently an appealing approach to some, but doesn't suit all scenarios. See spray/spray#1045. |
I've started working on this issue, and I'm sort of stuck on some questions I can't resolve myself. My overall idea is to create a This implementation fails at one of the tests in Besides that, I have 2 questions about this approach in general. First of all, do you think it makes sense? I mean, the Secondly, if the I hope my questions make sense. I'm on vacations now and will be able to dedicate more time to this issue for the next week, especially if I'll have some guidance from the Akka core team. :) |
Another thought has just occurred to me. The existing |
This had some progress in akka/akka#20135 but we decided together that it wasn't quite there yet. Would be awesome to see it be picked up again (@RomanIakovlev again perhaps if he has time?) |
Have you some workaround, please? It is a hard stopper. |
Here's the options: |
Thanks. I know the a) is the best for akka, but resources are limited at the moment resulting in preferring the b). |
Take a look at this for implementing retries: akka/akka-stream-contrib#26 |
currently in contrib, but seems like useful for many things so we might want to migrate it to akka-streams and consider adding it to the HTTP client API somehow. |
Here's a stub for redirection support and how things could be wired: package akka.http
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.headers.Location
import akka.http.scaladsl.model.{ HttpMethods, HttpRequest, HttpResponse, StatusCodes }
import akka.stream.ActorMaterializer
import scala.concurrent.{ ExecutionContext, Future }
object RichHttpClient {
type HttpClient = HttpRequest ⇒ Future[HttpResponse]
def redirectOrResult(singleRequest: HttpClient)(response: HttpResponse): Future[HttpResponse] =
response.status match {
case StatusCodes.Found | StatusCodes.MovedPermanently | StatusCodes.SeeOther ⇒
val newUri = response.header[Location].get.uri
// TODO: add debug logging
// change to GET method as allowed by https://tools.ietf.org/html/rfc7231#section-6.4.3
// TODO: keep HEAD if the original request was a HEAD request as well?
// TODO: do we want to keep something of the original request like custom user-agents, cookies
// or authentication headers?
singleRequest(HttpRequest(method = HttpMethods.GET, uri = newUri))
// TODO: what to do on an error? Also report the original request/response?
// TODO: also handle 307, which would require resending POST requests
case _ ⇒ Future.successful(response)
}
def httpClientWithRedirect(client: HttpClient)(implicit ec: ExecutionContext): HttpClient = {
lazy val redirectingClient: HttpClient =
req ⇒ client(req).flatMap(redirectOrResult(redirectingClient)) // recurse to support multiple redirects
redirectingClient
}
}
object SingleRequestWithRedirect extends App {
implicit val system = ActorSystem()
import system.dispatcher
implicit val mat = ActorMaterializer()
val simpleClient = Http().singleRequest(_: HttpRequest)
val redirectingClient = RichHttpClient.httpClientWithRedirect(simpleClient)
val request = HttpRequest(uri = "http://goggle.de")
simpleClient(request).onComplete(res ⇒ println(s"Without redirection: $res"))
redirectingClient(request).onComplete(res ⇒ println(s"With redirection: $res"))
} I added lots of TODOs where a general solution would have to actually support solutions. |
Thank you @jrudolph , for sharing general solution it helped me :) |
To help answering the |
The low level in Akka is like that actually. Since Flow[Request, Response] means there can be multiple responses, we simply don't need the List as it's already represented by how Flows work. |
BTW, I guess recursion must be limited. I use this workaround: private val maxRedirCount = 20
def httpRequire(req: HttpRequest, count: Int = 0)(implicit system: ActorSystem, mat: Materializer): Future[HttpResponse] = {
implicit val ec: ExecutionContext = system.dispatcher
Http().singleRequest(req).flatMap { resp =>
resp.status match {
case StatusCodes.Found => resp.header[headers.Location].map { loc =>
val locUri = loc.uri
val newUri = req.uri.copy(scheme = locUri.scheme, authority = locUri.authority)
val newReq = req.copy(uri = newUri)
if (count < maxRedirCount) httpRequire(newReq, count + 1) else Http().singleRequest(newReq)
}.getOrElse(throw new RuntimeException(s"location not found on 302 for ${req.uri}"))
case _ => Future(resp)
}
}
} |
what is the reason for only replacing scheme and authority part of the URI? |
@usamec It does work at my use cases. |
@gaydenko And yet it does not work on example from wikipedia: https://en.wikipedia.org/wiki/HTTP_301 I would copy the whole URI and handle all the 30x status codes. |
@usamec You can modify that workaround the way you want. In common case there isn't single rule all redirectors follow to, and I was just focused on the problem in hands. Again, at my case those few line of code do the job. You can use |
please note that this won't work for the case when uri in location is relative and you don't use host connection pool (just encountered this case). Also, as I understand docs, you forgot to dicard redirect response's bytes so I assume, it should look like: response.discardEntityBytes().future().flatMap(_ =>
singleRequest(HttpRequest(method = HttpMethods.GET, uri = newUri))
) |
A slightly modified version of @jrudolph 's code (only new thing added: discardEntityBytes):
Available as: |
Hello everyone from 2020. |
Hello everyone from 2021. |
if anyone has a problem with the redirects in laravel, do check your .htaccess and see if you have any 301 redirect like: As redirect in akka is not following though, this will terminate the request and wan't process the redirect. |
Hello everyone from 2023. |
Monday Sep 29, 2014 at 08:00 GMT
Originally opened as akka/akka#15990
The high-level client-side API should support some form of configurable redirection handling.
Several things need to be taken into account:
Before starting on this, it would make sense to go through the mailing list / spray tickets to find further cases we might need to support.
A first solution could only support redirections to the same domain, where keeping certain headers may be safer (but: cookies with path constraints set).
Maybe also consider how browsers deal with redirections.
This is part of the bigger initiative to support a high-level HTTP client interface as tracked as #16856.
/cc @sirthias
The text was updated successfully, but these errors were encountered: