Skip to content

Commit 431bdb2

Browse files
committed
[suave] fixes #1
1 parent 7bb028a commit 431bdb2

4 files changed

Lines changed: 99 additions & 27 deletions

File tree

.semver

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
:major: 0
33
:minor: 4
4-
:patch: 1
4+
:patch: 2
55
:special: ''
66
:metadata: ''

src/logibit.hawk.suave.tests/Hawk.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ let making_request =
4646
let sample_app =
4747
Hawk.authenticate
4848
settings
49+
Hawk.bind_req
4950
(fun (attr, creds, user) -> OK (sprintf "authenticated user '%s'" user.real_name))
5051
(fun err -> UNAUTHORIZED (err.ToString()))
5152

src/logibit.hawk.suave/Hawk.fs

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ type SHttpMethod = HttpMethod
99
open logibit.hawk
1010
open logibit.hawk.Types
1111
open logibit.hawk.Server
12-
open logibit.hawk.Choice
1312

1413
module private Impl =
1514
open Microsoft.FSharp.Reflection
@@ -46,47 +45,88 @@ module private Impl =
4645
[<Literal>]
4746
let HawkDataKey = "logibit.hawk.data"
4847

49-
let auth_ctx (s : Settings<'a>) =
50-
fun ({ request = s_req } as ctx) ->
51-
52-
let ub = UriBuilder (s_req.url)
53-
ub.Host <- s_req.host.value
54-
55-
Binding.header "authorization" Choice1Of2 s_req
56-
>>= (fun header ->
57-
Binding.header "host" Choice1Of2 s_req
58-
>>- fun host -> header, host)
48+
/// ReqFactory :: Settings<'a> -> HttpContext -> Choice<Req, string>
49+
///
50+
/// You can bind the last argument to a function
51+
/// that maps your request changing function into the choice. Or in code:
52+
///
53+
/// let plx_goto_8080_MR s =
54+
/// bind_req s
55+
/// // when you've bound the request, apply the following function to
56+
/// // the return value (the Choice of req or a string error)
57+
/// >> (fun mreq ->
58+
/// // map over the OK result (non error case), see
59+
/// // https://github.com/logibit/logibit.hawk#logibithawkchoice
60+
/// mreq >>- (fun req ->
61+
/// // and change the port so we can find our way:
62+
/// { req with port = Some 8080us }))
63+
type ReqFactory<'a> = Settings<'a> -> HttpContext -> Choice<Req, string>
64+
65+
open logibit.hawk.Choice // Choice's binding of >>=
66+
67+
let bind_req (s : Settings<'a>)
68+
({ request = s_req } as ctx)
69+
: Choice<Req, string> =
70+
71+
let ub = UriBuilder (s_req.url)
72+
ub.Host <- s_req.host.value
73+
74+
Binding.header "authorization" Choice1Of2 s_req
75+
>>= (fun header ->
76+
Binding.header "host" Choice1Of2 s_req
77+
>>- fun host -> header, host)
78+
>>- (fun (auth, host) ->
79+
{ ``method`` = Impl.from_suave_method s_req.``method``
80+
uri = ub.Uri
81+
authorisation = auth
82+
payload = if s_req.raw_form.Length = 0 then None else Some ctx.request.raw_form
83+
host = None
84+
port = None
85+
content_type = "content-type" |> HttpRequest.header ctx.request })
86+
87+
// Example functor of the bind_req function:
88+
//let bind_req' s =
89+
// bind_req s >> (fun mreq -> mreq >>- (fun req -> { req with port = Some 8080us }))
90+
91+
let auth_ctx (s : Settings<'a>) (f_req : ReqFactory<'a>) =
92+
fun ctx ->
93+
f_req s ctx
5994
>>@ AuthError.Other
60-
>>= (fun (auth, host) ->
61-
let req =
62-
{ ``method`` = Impl.from_suave_method s_req.``method``
63-
uri = ub.Uri
64-
authorisation = auth
65-
payload = if s_req.raw_form.Length = 0 then None else Some ctx.request.raw_form
66-
host = None
67-
port = None
68-
content_type = "content-type" |> HttpRequest.header ctx.request }
69-
Server.authenticate s req)
95+
>>= Server.authenticate s
96+
97+
let auth_ctx' s = auth_ctx s bind_req
7098

7199
open Suave.Http // this changes binding of >>=
72100

73-
/// Authenticate the request with the given settings and a
74-
/// continuation functor for both the successful case and the
75-
/// unauthorised case.
101+
/// Authenticate the request with the given settings, and a request
102+
/// getting function (ReqFactory) and then a continuation functor for
103+
/// both the successful case and the unauthorised case.
76104
///
77105
/// This will also set `HawkDataKey` in the `user_state` dictionary.
78-
let authenticate (s : Settings<_>)
106+
///
107+
/// You might want to use authenticate' unless you're running behind
108+
/// a load balancer and need to replace your `bind_req` function (in this
109+
/// module) with something of your own.
110+
///
111+
/// Also see the comments on the ReqFactory type for docs on how to contruct
112+
/// your own Req value, or re-map the default one.
113+
let authenticate (s : Settings<'a>)
114+
(f_req : ReqFactory<'a>)
79115
(f_cont : _ -> WebPart)
80116
(f_err : AuthError -> WebPart)
81117
: WebPart =
82118
fun ctx ->
83-
match auth_ctx s ctx with
119+
match auth_ctx s f_req ctx with
84120
| Choice1Of2 res ->
85121
(Writers.set_user_data HawkDataKey res
86122
>>= f_cont res) ctx
87123
| Choice2Of2 err ->
88124
f_err err ctx
89125

126+
/// Like `authenticate` but with the default request factory function.
127+
let authenticate' s =
128+
authenticate s bind_req
129+
90130
module HttpContext =
91131

92132
/// Find the Hawk auth data from the context.

src/logibit.hawk/Server.fs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,37 @@ type Req =
102102
/// URI to the `authenticate` function.
103103
port : Port option }
104104

105+
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
106+
module Req =
107+
108+
let ``method``_ =
109+
(fun x -> x.``method``),
110+
fun v (x : Req) -> { x with ``method`` = v }
111+
112+
let uri_ =
113+
(fun x -> x.uri),
114+
fun v (x : Req) -> { x with uri = v }
115+
116+
let authorisation_ =
117+
(fun x -> x.authorisation),
118+
fun v (x : Req) -> { x with authorisation = v }
119+
120+
let payload_ =
121+
(fun x -> x.payload),
122+
fun v (x : Req) -> { x with payload = v }
123+
124+
let content_type_ =
125+
(fun x -> x.content_type),
126+
fun v (x : Req) -> { x with content_type = v }
127+
128+
let host_ =
129+
(fun x -> x.host),
130+
fun v (x : Req) -> { x with host = v }
131+
132+
let port_ =
133+
(fun x -> x.port),
134+
fun v (x : Req) -> { x with port = v }
135+
105136
/// Authentication settings
106137
type Settings<'a> =
107138
{ /// The clock to use for getting the time.

0 commit comments

Comments
 (0)