Skip to content

Commit 6afc6e2

Browse files
no-longer-on-githu-bowickstrom
authored andcommitted
Add function for parsing request URIs
1 parent 795e939 commit 6afc6e2

File tree

5 files changed

+67
-3
lines changed

5 files changed

+67
-3
lines changed

src/Hyper/Form/Urlencoded.purs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Hyper.Form.Urlencoded
66

77
import Prelude
88
import Control.Monad.Error.Class (throwError)
9+
import Data.Array as Array
910
import Data.Either (Either)
1011
import Data.Maybe (Maybe(Just, Nothing))
1112
import Data.String (split, joinWith, Pattern(Pattern))
@@ -26,6 +27,7 @@ toTuple kv =
2627

2728
parseUrlencoded :: String Either String (Array (Tuple String (Maybe String)))
2829
parseUrlencoded = split (Pattern "&")
30+
>>> Array.filter (_ /= "")
2931
>>> map (split (Pattern "="))
3032
>>> map toTuple
3133
>>> sequence

src/Hyper/Node/Server.purs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@ import Control.Monad.Eff.Console (CONSOLE, log)
2727
import Control.Monad.Eff.Exception (EXCEPTION, Error, catchException, error)
2828
import Control.Monad.Error.Class (throwError)
2929
import Data.Either (Either(..), either)
30+
import Data.Lazy (defer)
3031
import Data.Maybe (Maybe(..))
3132
import Data.Newtype (unwrap)
3233
import Data.Tuple (Tuple(..))
3334
import Hyper.Conn (Conn)
3435
import Hyper.Middleware (Middleware, evalMiddleware, lift')
3536
import Hyper.Middleware.Class (getConn, modifyConn)
3637
import Hyper.Port (Port(..))
37-
import Hyper.Request (class ReadableBody, class Request, class StreamableBody, RequestData, readBody)
38+
import Hyper.Request (class ReadableBody, class Request, class StreamableBody, RequestData, parseUrl, readBody)
3839
import Hyper.Response (class ResponseWritable, class Response, ResponseEnded, StatusLineOpen)
3940
import Hyper.Status (Status(..))
4041
import Node.Buffer (BUFFER, Buffer)
@@ -245,6 +246,7 @@ mkHttpRequest request =
245246
headers = HTTP.requestHeaders request
246247
requestData =
247248
{ url: HTTP.requestURL request
249+
, parsedUrl: defer \_ -> parseUrl (HTTP.requestURL request)
248250
, headers: headers
249251
, method: Method.fromString (HTTP.requestMethod request)
250252
, contentLength: StrMap.lookup "content-length" headers

src/Hyper/Request.purs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module Hyper.Request
22
( class Request
33
, RequestData
4+
, ParsedUrl
5+
, parseUrl
46
, getRequestData
57
, class BaseRequest
68
, class ReadableBody
@@ -9,20 +11,44 @@ module Hyper.Request
911
, streamBody
1012
) where
1113

14+
import Prelude
15+
import Data.Array as Array
16+
import Data.String as String
17+
import Data.Bifunctor (lmap)
1218
import Data.Either (Either)
1319
import Data.HTTP.Method (CustomMethod, Method)
14-
import Data.Maybe (Maybe)
20+
import Data.Lazy (Lazy)
21+
import Data.Maybe (Maybe, fromMaybe)
1522
import Data.StrMap (StrMap)
23+
import Data.Tuple (Tuple)
1624
import Hyper.Conn (Conn)
25+
import Hyper.Form.Urlencoded (parseUrlencoded)
1726
import Hyper.Middleware (Middleware)
1827

1928
type RequestData =
2029
{ url :: String
30+
, parsedUrl :: Lazy ParsedUrl
2131
, contentLength :: Maybe Int
2232
, headers :: StrMap String
2333
, method :: Either Method CustomMethod
2434
}
2535

36+
type ParsedUrl =
37+
{ path :: Array String
38+
, query :: Either String (Array (Tuple String (Maybe String)))
39+
}
40+
41+
parseUrl :: String -> ParsedUrl
42+
parseUrl url =
43+
let
44+
idx = fromMaybe (String.length url) $ String.indexOf (String.Pattern "?") url
45+
rawPath = String.take idx url
46+
rawQuery = String.drop (idx + 1) url
47+
path = Array.filter (_ /= "") $ String.split (String.Pattern "/") rawPath
48+
query = lmap (const rawQuery) $ parseUrlencoded rawQuery
49+
in
50+
{path, query}
51+
2652
class Request req m where
2753
getRequestData
2854
:: forall res c

src/Hyper/Test/TestServer.purs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Data.Foldable (fold)
1313
import Data.Function ((<<<))
1414
import Data.Functor (map)
1515
import Data.HTTP.Method (CustomMethod, Method(..))
16+
import Data.Lazy (defer)
1617
import Data.Maybe (Maybe(Nothing, Just))
1718
import Data.Monoid (mempty, class Monoid)
1819
import Data.Newtype (class Newtype, unwrap)
@@ -22,7 +23,7 @@ import Hyper.Conn (Conn)
2223
import Hyper.Header (Header)
2324
import Hyper.Middleware (lift')
2425
import Hyper.Middleware.Class (getConn, modifyConn)
25-
import Hyper.Request (class ReadableBody, class Request)
26+
import Hyper.Request (class ReadableBody, class Request, parseUrl)
2627
import Hyper.Response (class ResponseWritable, class Response)
2728
import Hyper.Status (Status)
2829

@@ -55,6 +56,7 @@ instance requestTestRequest :: Monad m => Request TestRequest m where
5556
getRequestData =
5657
getConn :>>= \{ request: TestRequest r } ->
5758
ipure { url: r.url
59+
, parsedUrl: defer \_ -> parseUrl r.url
5860
, contentLength: Just (String.length r.body)
5961
, method: r.method
6062
, headers: r.headers

test/Hyper/RequestSpec.purs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module Hyper.RequestSpec where
2+
3+
import Prelude
4+
import Data.Either (Either(..))
5+
import Data.Maybe (Maybe(..))
6+
import Data.Tuple.Nested ((/\))
7+
import Hyper.Request (parseUrl)
8+
import Test.Spec (Spec, it, describe)
9+
import Test.Spec.Assertions (shouldEqual)
10+
11+
spec :: forall e. Spec e Unit
12+
spec =
13+
describe "Hyper.Request" do
14+
it "parses the root URL" do
15+
let result = parseUrl "/"
16+
result.path `shouldEqual` []
17+
result.query `shouldEqual` Right []
18+
19+
it "parses non-root URLs" do
20+
let result = parseUrl "/foo/bar"
21+
result.path `shouldEqual` ["foo", "bar"]
22+
result.query `shouldEqual` Right []
23+
24+
it "parses URLs with query strings" do
25+
let result = parseUrl "/foo/bar?abc=def=ghi"
26+
result.path `shouldEqual` ["foo", "bar"]
27+
result.query `shouldEqual` Left "abc=def=ghi"
28+
29+
it "parses URLs with formatted query strings" do
30+
let result = parseUrl "/foo/bar?abc=def&ghi"
31+
result.path `shouldEqual` ["foo", "bar"]
32+
result.query `shouldEqual` Right ["abc" /\ Just "def", "ghi" /\ Nothing]

0 commit comments

Comments
 (0)