On this page:
send/  suspend/  protect
send/  forward/  protect
send/  suspend/  dispatch/  protect
redirect/  get/  protect
redirect/  get/  forget/  protect

5 Continuations

 (require koyo/continuation) package: koyo-lib

Continuations in a web context are super valuable when it comes to quickly prototyping certain functionality. However, because it stores continuation ids within URLs, the web server is succeptible to session hijacking. This is by design. For some use cases it makes sense to allow users to share their URLs and have someone else be able to pick up from where they left off. In most web applications, though, that’s not what you want.

Let me illustrate the issue with an example. Say you have an e-commerce website and you implement your "add to cart" buttons using continuations. If you don’t protect your continuations, then a user could copy the URL for that button and give it to someone else; that other person would then inherit all of the state associated with that continuation. You might think that your users are unlikely to do that, but there are cases, such as via Referer headers, in which browsers can leak information about URLs in a web application.

To guard against the aforementioned issues, this module provides variants of the web server’s continuation-related functions that protect themselves against being hijacked. They do this by associating a random session cookie with each continuation that is captured. When a continuation is called and the visitor’s session does not contain said cookie, current-continuation-mismatch-handler is run instead of the target continuation function.

These parameters control the Path= attribute and the Secure= attribute, respectively, of the continuation key cookie.

This parameter holds the handler that is run whenever a continuation is called with an invalid continuation key in the request. The default implementation short-circuits the request and redirects the user to the base path for that continuation, skipping its handler.

If you install a custom mismatch handler, you must avoid modifying the user session within it. The session that will get modified is the session of the original user, not of the "attacker", which is probably not what you want.


(-> (-> request? response?)
    (-> request? response?))
(current-continuation-wrapper wrapper)  void?
  wrapper : 
(-> (-> request? response?)
    (-> request? response?))
Since continuations are executed outside of the standard request - response lifecycle, this means that any middleware you set up on your dispatchers will not wrap your continuation handlers. To get around this, you have to register your middleware stack with this parameter.


((wrap-protect-continuations handler) req)  response?

  handler : (-> request? response?)
  req : request?
This function sets up the continuation key and updates all responses that pass through it to include the continuation key cookie.

Without this, all protected continuations will fail out so don’t forget to add it to your middleware stack.

Middleware that themselves wrap wrap-protect-continuations will wrap the mismatch handler. This means you have to be careful not to wrap the middleware inside other middleware that modify the user session. Nor should your mismatch handler modify the session, because the session it modifies will be the one of the original user, not the "attacker."


(send/suspend/protect p)  request?

  p : (-> string? can-be-response?)


(send/forward/protect p)  request?

  p : (-> string? can-be-response?)


(send/suspend/dispatch/protect p)  any

  p : (-> (-> (-> request? any) string?) can-be-response?)


(redirect/get/protect [#:headers headers])  request?

  headers : (listof header?) = null


(redirect/get/forget/protect [#:headers headers])  request?

  headers : (listof header?) = null
Protected variants of the continuation-related functions in Web Interaction.