let vdir (s : String) choose_list (ctx : HttpContext) = if ctx.request.url.StartsWith(s) then let us = if ctx.user_state.ContainsKey("original_url") then ctx.user_state else ctx.user_state.Add("original_url", ctx.request.url) let new_req = { ctx with request={ctx.request with url=ctx.request.url.Substring(s.Length)} ; user_state = us } in choose choose_list new_req else None let subapp = vdir "/sub" [ url "/abc" >>= OK "subapp abc route" warbler(fun {request=req ; user_state=us} -> OK ( sprintf "subapp - url %s - original url %A" req.url us)) ]

Now when subapp is loaded inside an application’s choose chain, if /sub/xyz is visited, the browser will print:

subapp - url /xyz - original url map [(“original_url”, “/sub/xyz”)]

Is this an abuse of the user_state list maintained by Suave?

As an alternative, we can define the vdir function to take a WebPart:

let vdir (s : String) (app : WebPart) (ctx : HttpContext) = if ctx.request.url.StartsWith(s) then let us = if ctx.user_state.ContainsKey("original_url") then ctx.user_state else ctx.user_state.Add("original_url", ctx.request.url) let new_req = { ctx with request={ctx.request with url=ctx.request.url.Substring(s.Length)} ; user_state = us } in app new_req else None let subapp = vdir "/sub" <| choose [ url "/abc" >>= OK "def" warbler(fun {request=req ; user_state=us} -> OK ( sprintf "subapp with choose - url %s - original url %A" req.url us)) ]