How to redirect internally in Ktor? - redirect

I'm wondering if there's a way to do an internal redirect, re-route, or response forwarding inside Ktor.
call.respondRedirect("relative/url")
sends a HTTP 302 or 301 depending on the permantent: Boolean flag. I'm looking for something that would do the same without using HTTP, just internally in Ktor. Here's some pseudo-routing of what I want to achieve:
get("/foo") {
if (call.parameters["something"] != null) {
call.respondText("Yay, something!")
} else {
call.respondRedirect("/bar") // except without relying on client to handle HTTP redirects.
}
}
get("/bar") {
call.respondText("No thing :(")
}
The goal is that the client shouldn't make 2 requests, and shouldn't be aware of the redirection happening.
NB: I'm aware I can extract a function for /bar's body and invoke it, instead of responsdRedirect. However, I want to make Ktor handle it so that it goes through all the necessary lifecycle and pipeline with all the interceptors. This is to make sure it is handled as if it was an external request, except the network roundtrip.
I'm looking for something like Express.js' req.app.handle(req, res) as shown in the first half of this answer: https://stackoverflow.com/a/48790319/253468. A potential solution I couldn't understand yet is something like TestApplicationEngine.handleRequest (in io.ktor:ktor-server-test-host) is doing with pipeline.execute. I guess I could invoke call.application.execute(), the question is how to construct the ApplicationCall object then. Note this is for production use, so no TestApplicationCall.

You can do similar thing in Ktor by using call.application.execute function with cloned call object. For convenience let's define extension function for doing internal redirects:
suspend fun ApplicationCall.redirectInternally(path: String) {
val cp = object: RequestConnectionPoint by this.request.local {
override val uri: String = path
}
val req = object: ApplicationRequest by this.request {
override val local: RequestConnectionPoint = cp
}
val call = object: ApplicationCall by this {
override val request: ApplicationRequest = req
}
this.application.execute(call)
}
Here it creates a copy of an ApplicationCall object with the replaced path for a request. I use delegates to avoid boilerplate code. You can use redirectInternally function like this:
get("/foo") {
call.redirectInternally("/bar")
}

Related

intercept lift rest request and verify parameters

I want to validate REST requests (GET and PUT) in LIFT before processing it. i.e. i need to check whether requests has a parameter requestor. if not need to response with exception saying missing parameter. Could you please let me know how to do this.
There are a few things you could do. The two I would try would be a helper function that would wrap your Rest call, something like:
def checkParam(r:Req):Boolean = {
r.param("paramName").isDefined
}
def requireParams[T<:LiftResponse](r:Req)(methodBody: => T):LiftResponse = {
if(checkParam(r))
methodBody
else
InMemoryResponse("Parameters not specified".getBytes(), List("content-type" -> "text/plain"), Nil, 500)
}
The function will check for the parameters and return an error if it doesn't work, or execute the call if it does. In your Rest call, you would use it like:
case "location" :: Nil Get req => requireParams(req){
//your rest body
}
Alternately, you could probably use a guard on the entire RestHelper assuming you wanted to check every method call, something like this might work:
val ensureParams: PartialFunction[Req, Unit] = {
case r if (r.get_? || r.put_?) && checkParam(r) =>
case r if (!r.get_? && !r.put_?) =>
}
and then guard your RestHelper instance in Boot with:
LiftRules.dispatch.append(ensureParams guard YourRestHelper)
I haven't tested the above code, so there may be some mistakes - but hopefully it should help get you started.

Spray routing works for single slash but nothing else

So i have asked about this before and have changed a lot of code around.
Spray Routing Doesn't match anything
Now I am executing my functions that return HTTPresponses insided detach() blocks so that i dont block. These then are completed and return to the client, but I still can't seem to get my routing to work.
In my tests, a request to a single slash works fine, but anything else, such as this create user path shown below fails. I can't seem to figure out why, and spray routing uses so many constructs I'm having a hard time figuring out how the system works well enough to find out whats happening.
I tried inserting logRequest blocks around certain paths thinking that might show me whats happening, but none of them seem to get hit. Any help would be greatly appreciated.
val route: Route = {
host("fakebook.com", "www.fakebook.com") {
pathSingleSlash {
complete("pong")
} ~
pathPrefix("users") { req =>
path("newuser") {
put {
detach() {
complete(genericPut(CreateUser(req.request)))
}
}
} ~
... rest of routing
And here is what my scalatests look like, the simple Put passes, but the put with newuser doesn't
val createUserSuccessRequest = Put(Uri("http://www.fakebook.com/users/newuser") withQuery(F_User.lastNameString -> "Doe", F_User.firstNameString -> "John", F_User.bioString -> "i like foobar",
F_User.ageString -> "42", F_User.dobString -> dateFormatter.format(new Date(1970 - 1900, 5, 7))))
"The FakeBook route" when {
"When sending a single slash request" should {
"respond with a simple pong" in {
Get() ~> logRequestResponse("plain get final request and response")(sealRoute(route)) ~> check {
assert(responseAs[String] == "pong")
}
}
}
"Running route to create a user with PUT" should {
"forward a message to the backbone to create a new user" in {
createUserSuccessRequest ~> logRequest("create user final request and response"){sealRoute(route)} ~> check {
expectMsg(CreateUser(createUserSuccessRequest))
}
}
}
}
For anyone else trying to solve this issue:
a lot of these directives actually DONT extract anything, so having the lambda inputs i have like req => and req2 => will not work.
It turns out, spray routing is designed so that you never have to touch the RequestContext as I have done with my functions (which is why I try to access it). They extract only the useful data. Rather than do things as I should and change my function signatures, i am going to (for now) do a hotfix that has worked.
if you absolutely must have the requestcontext, so long as you don't break it somehow, you can extract it by making your own extraction directive like so
val extractRequestContext = extract(x => x) and wrap your code in that
I did this
path("somepath") {
detach() {
extractRequestContext { request => complete(someFunc(request)) }
}
}
In the future I should learn to use the DSL more correctly and extract what I need from the request context using directives and pass THOSE to the functions

Is there a quick built in way to forward a request in the scala Play framework

I'm looking for something like
def proxy = Action.async { implicit req =>
//do something with req
val newRequest = req.map( r = r.path = "http://newurl");
forward(newRequest)
}
I saw that there is a redirect method but that only allows me to pass the request parameters and not everything else, headers, etc.
I am hoping there is something built in so I don't have to build it myself.
I'm not sure if this meets your requirements, but have you had a look into Play's WS.
The action forwardTo gets an url, fetches the according page and returns it as this request's response. It's not exactly like an forward in the Spring framework but it does the job for me.
/**
* Like an internal redirect or an proxy. The URL in the browser doesn't
* change.
*/
public Promise<Result> forwardTo(String url) {
Promise<WS.Response> response = WS.url(url).get();
return response.map(new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
// Prevent browser from caching pages - this would be an
// security issue
response().setHeader("Cache-control", "no-cache, no-store");
return ok(response.getBody()).as("text/html");
}
});
}
(I'm using Play 2.2.3)

Basic Play framework routing and web sockets example

I'm trying to learn how to use web sockets in Play 2.1, and I'm having trouble getting the web socket URL to work with my app's routing configuration. I started with a new Play application and the Play framework documentation on websockets.
Here is my conf/routes:
# Home page
GET / controllers.Application.index
# Websocket test site
GET /wstest controllers.Application.wstest
Then I added the wstest function to my controller class:
object Application extends Controller {
def index = Action {
Ok(views.html.index("Websocket Test"))
}
def wstest = WebSocket.using[String] { request =>
// Log events to the console
val in = Iteratee.foreach[String](println).mapDone { _ =>
Logger.info("Disconnected")
}
// Send a single 'Hello!' message
val out = Enumerator("Hello!")
(in, out)
}
}
However, so far, I can only access the websocket with the URL ws://localhost:9000/wstest (using the sample code at websocket.org/echo.html). I was looking at the sample/scala/websocket-chat app that comes with the Play framework, and it uses the routing configuration file to reference the websocket, like this:
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var chatSocket = new WS("#routes.Application.chat(username).webSocketURL()")
I tried replacing my websocket URL with #routes.Application.wstest.webSocketURL() and #routes.Application.wstest. The first one doesn't compile. The second one compiles, but the client and server don't exchange any messages.
How can I use my Play routing configuration to access this websocket? What am I doing wrong here?
Edit
Here is a screenshot of my compilation error, "Cannot find any HTTP Request Header here":
Without the compiler error it's hard to guess what might be the problem.
Either you have to use parens because of the implicit request, i.e. #routes.Application.wstest().webSocketURL(), or you have no implicit request in scope which is needed for the webSocketURL call.
Marius is right that there was no implicit request in scope. Here's how to get it in scope:
Update the index function in the controller:
def index = Action { implicit request =>
Ok(views.html.index("Websocket Test"))
}
Add the request as a curried parameter to index.scala.html:
#(message: String)(implicit request: RequestHeader)
#main(message) {
<script>
var output;
function init() {
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket("#routes.Application.wstest.webSocketURL()");
.
.
.
And now the RequestHeader is in scope.

Simple use of dispatch

I'm about to set up a stupid play/scala app whose only job will be to make some http call once it receives calls itself
GET /abracadabra controllers.Application.abracadabra(stuff: String)
and then
def abracadabra(stuff: String) = Action {
Logger.info("called for stuff: "+stuff);
// call this other URL with 'stuff' as get parameter
// log http return status code and return Ok/200 anyways
}
Now for the second (commented) part I thought about using Dispatch.
I've read the docs but I can't just figure out how to use Promises and all that.
If anybody could point me to some sample code or something, it will be much appreciated
Since Play! has a built in Async library, you should probably go ahead and use that unless there's a feature in Dispatch that you specifically need.
Here's a short example:
def abracadabra(stuff: String) = Action {
Logger.info("called for stuff: "+stuff);
Async {
WS.url("http://stackoverflow.com/").get().map { response =>
Ok("I got it: " + response)
}
}
}
The documentation is here: https://github.com/playframework/Play20/wiki/ScalaWS